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 <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 :
78 : // Using/Aliasing
79 : using namespace DataVectorTypes;
80 : using namespace DataBSDFWindow;
81 : using namespace DataHeatBalance;
82 : using namespace DataZoneEquipment;
83 : using namespace DataLoopNode;
84 : using namespace Psychrometrics;
85 : using namespace DataEnvironment;
86 : using namespace Window;
87 :
88 : Array1D_string const cExtBoundCondition({-6, 0}, {"KivaFoundation", "FCGround", "OSCM", "OSC", "OSC", "Ground", "ExternalEnvironment"});
89 :
90 43646 : Surface2D::Surface2D(ShapeCat const shapeCat, int const axis, Vertices const &v, Vector2D const &vl, Vector2D const &vu)
91 43646 : : axis(axis), vertices(v), vl(vl), vu(vu)
92 : {
93 43646 : size_type const n(vertices.size());
94 43646 : assert(n >= 3);
95 :
96 : // Reverse vertices order if clockwise
97 : // If sorting by y for slab method can detect clockwise faster by just comparing edges at bottom or top-most vertex
98 43646 : Real64 area(0.0); // Actually 2x the signed area
99 219071 : for (Vertices::size_type i = 0; i < n; ++i) {
100 175425 : Vector2D const &v(vertices[i]);
101 175425 : Vector2D const &w(vertices[(i + 1) % n]);
102 175425 : area += (v.x * w.y) - (w.x * v.y);
103 : }
104 43646 : if (area < 0.0) std::reverse(vertices.begin() + 1, vertices.end()); // Vertices in clockwise order: Reverse all but first
105 :
106 : // Set up edge vectors for ray--surface intersection tests
107 43646 : edges.reserve(n);
108 219071 : for (Vertices::size_type i = 0; i < n; ++i) {
109 175425 : edges.push_back(vertices[(i + 1) % n] - vertices[i]);
110 : }
111 43646 : if (shapeCat == ShapeCat::Rectangular) { // Set side length squared for ray--surface intersection tests
112 38008 : assert(n == 4u);
113 38008 : s1 = edges[0].magnitude_squared();
114 38008 : s3 = edges[3].magnitude_squared();
115 5638 : } else if ((shapeCat == ShapeCat::Nonconvex) || (n >= nVerticesBig)) { // Set up slabs
116 197 : assert(n >= 4u);
117 197 : slabYs.reserve(n);
118 2025 : for (size_type i = 0; i < n; ++i)
119 1828 : slabYs.push_back(vertices[i].y);
120 197 : std::sort(slabYs.begin(), slabYs.end()); // Sort the vertex y coordinates
121 394 : auto const iClip(std::unique(slabYs.begin(), slabYs.end())); // Remove duplicate y-coordinate elements
122 197 : slabYs.erase(iClip, slabYs.end());
123 197 : slabYs.shrink_to_fit();
124 884 : for (size_type iSlab = 0, iSlab_end = slabYs.size() - 1; iSlab < iSlab_end; ++iSlab) { // Create slabs
125 687 : Real64 xl(std::numeric_limits<Real64>::max());
126 687 : Real64 xu(std::numeric_limits<Real64>::lowest());
127 687 : Real64 const yl(slabYs[iSlab]);
128 687 : Real64 const yu(slabYs[iSlab + 1]);
129 687 : slabs.push_back(Slab(yl, yu));
130 687 : Slab &slab(slabs.back());
131 : using CrossEdge = std::tuple<Real64, Real64, size_type>;
132 : using CrossEdges = std::vector<CrossEdge>;
133 687 : CrossEdges crossEdges;
134 8073 : for (size_type i = 0; i < n; ++i) { // Find edges crossing slab
135 7386 : Vector2D const &v(vertices[i]);
136 7386 : Vector2D const &w(vertices[(i + 1) % n]);
137 7386 : if (((v.y <= yl) && (yu <= w.y)) || // Crosses upward
138 6410 : ((yu <= v.y) && (w.y <= yl))) // Crosses downward
139 : {
140 1952 : Edge const &e(edges[i]);
141 1952 : assert(e.y != 0.0);
142 1952 : Real64 const exy(e.x / e.y);
143 1952 : Real64 const xb(v.x + (yl - v.y) * exy); // x_bot coordinate where edge intersects yl
144 1952 : Real64 const xt(v.x + (yu - v.y) * exy); // x_top coordinate where edge intersects yu
145 1952 : xl = std::min(xl, std::min(xb, xt));
146 1952 : xu = std::max(xu, std::max(xb, xt));
147 1952 : crossEdges.push_back(std::make_tuple(xb, xt, i));
148 : }
149 : }
150 687 : slab.xl = xl;
151 687 : slab.xu = xu;
152 687 : assert(crossEdges.size() >= 2u);
153 687 : std::sort(crossEdges.begin(),
154 1374 : crossEdges.end(),
155 2724 : [](CrossEdge const &e1, CrossEdge const &e2) -> bool // Lambda to sort by x_mid
156 : {
157 2724 : return std::get<0>(e1) + std::get<1>(e1) <
158 2724 : std::get<0>(e2) + std::get<1>(e2); // Sort edges by x_mid: x_bot or x_top could have repeats with shared vertex
159 : });
160 : #ifndef NDEBUG // Check x_bot and x_top are also sorted
161 687 : Real64 xb(std::get<0>(crossEdges[0]));
162 687 : Real64 xt(std::get<1>(crossEdges[0]));
163 687 : 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
164 2639 : for (auto const &edge : crossEdges) { // Detect non-simple polygon with crossing edges
165 1952 : Real64 const xbe(std::get<0>(edge));
166 1952 : Real64 const xte(std::get<1>(edge));
167 1952 : assert(xb <= xbe + tol);
168 1952 : assert(xt <= xte + tol);
169 1952 : xb = xbe;
170 1952 : xt = xte;
171 687 : }
172 : #endif
173 687 : assert((shapeCat == ShapeCat::Nonconvex) || (crossEdges.size() == 2u));
174 2639 : for (auto const &edge : crossEdges) {
175 1952 : size_type const iEdge(std::get<2>(edge));
176 1952 : slab.edges.push_back(iEdge); // Add edge to slab
177 1952 : Vector2D const &e(edges[iEdge]);
178 1952 : assert(e.y != 0.0); // Constant y edge can't be a crossing edge
179 1952 : slab.edgesXY.push_back(e.y != 0.0 ? e.x / e.y : 0.0); // Edge inverse slope
180 687 : }
181 687 : assert(slab.edges.size() % 2 == 0u);
182 687 : assert(slab.edges.size() == slab.edgesXY.size());
183 687 : }
184 197 : }
185 43646 : }
186 :
187 : // Set Precomputed Parameters
188 46044 : void SurfaceData::set_computed_geometry()
189 : {
190 46044 : if (Vertex.size() >= 3) { // Skip no-vertex "surfaces"
191 43646 : shapeCat = computed_shapeCat();
192 43646 : plane = computed_plane();
193 43646 : surface2d = computed_surface2d();
194 : }
195 46044 : }
196 :
197 469676950 : Real64 SurfaceData::getInsideAirTemperature(EnergyPlusData &state, const int t_SurfNum) const
198 : {
199 : // SUBROUTINE INFORMATION:
200 : // AUTHOR Simon Vidanovic
201 : // DATE WRITTEN June 2016
202 : // MODIFIED na
203 : // RE-ENGINEERED na
204 :
205 : // PURPOSE OF THIS SUBROUTINE:
206 : // Routine calculates reference air temperature for given surface (refactoring from the code)
207 : //
208 : // NOTE: This routine has been copy/pasted in the past in several different modules with slight
209 : // modifications at some of those places. It is quite logical that reference air temperature
210 : // for the surface is calculated as public function of SurfaceData structure (class) and is
211 : // later called as needed. Note that SurfaceNum had to be passed to this routine because of
212 : // access to global array SurfTempEffBulkAir. I would propose refactoring where SurfTempEffBulkAir
213 : // is part of SurfaceData structure and instead of calling SurfTempEffBulkAir( SurfNum ) it should
214 : // be called Surface( SurfNum ).TempEffBulkAir (Simon Vidanovic)
215 :
216 469676950 : Real64 RefAirTemp = 0;
217 :
218 : // determine reference air temperature for this surface
219 469676950 : auto &thisSpaceHB = state.dataZoneTempPredictorCorrector->spaceHeatBalance(this->spaceNum);
220 469676950 : switch (state.dataSurface->SurfTAirRef(t_SurfNum)) {
221 2522441 : case RefAirTemp::ZoneMeanAirTemp: {
222 2522441 : RefAirTemp = thisSpaceHB.MAT;
223 2522441 : } break;
224 4023016 : case RefAirTemp::AdjacentAirTemp: {
225 4023016 : RefAirTemp = state.dataHeatBal->SurfTempEffBulkAir(t_SurfNum);
226 4023016 : } break;
227 0 : case RefAirTemp::ZoneSupplyAirTemp: {
228 : // determine ZoneEquipConfigNum for this zone
229 : // ControlledZoneAirFlag = .FALSE.
230 : // ZoneEquipConfigNum = ZoneNum;
231 : // check whether this zone is a controlled zone or not
232 0 : if (!state.dataHeatBal->Zone(Zone).IsControlled) {
233 0 : ShowFatalError(state,
234 0 : format("Zones must be controlled for Ceiling-Diffuser Convection model. No system serves zone {}",
235 0 : state.dataHeatBal->Zone(Zone).Name));
236 : // return;
237 : }
238 : // determine supply air conditions
239 0 : Real64 SumSysMCp = 0;
240 0 : Real64 SumSysMCpT = 0;
241 0 : auto &inletNodes = (state.dataHeatBal->doSpaceHeatBalance) ? state.dataZoneEquip->spaceEquipConfig(this->spaceNum).InletNode
242 0 : : state.dataZoneEquip->ZoneEquipConfig(Zone).InletNode;
243 0 : for (int nodeNum : inletNodes) {
244 0 : auto &inNode = state.dataLoopNodes->Node(nodeNum);
245 0 : Real64 CpAir = PsyCpAirFnW(thisSpaceHB.airHumRat);
246 0 : SumSysMCp += inNode.MassFlowRate * CpAir;
247 0 : SumSysMCpT += inNode.MassFlowRate * CpAir * inNode.Temp;
248 : }
249 : // a weighted average of the inlet temperatures.
250 0 : if (SumSysMCp > 0.0) {
251 : // a weighted average of the inlet temperatures.
252 0 : RefAirTemp = SumSysMCpT / SumSysMCp;
253 : } else {
254 0 : RefAirTemp = thisSpaceHB.MAT;
255 : }
256 0 : } break;
257 463131493 : default: {
258 : // currently set to mean air temp but should add error warning here
259 463131493 : RefAirTemp = thisSpaceHB.MAT;
260 463131493 : } break;
261 : }
262 :
263 469676950 : return RefAirTemp;
264 : }
265 :
266 8064 : Real64 SurfaceData::getOutsideAirTemperature(EnergyPlusData &state, const int t_SurfNum) const
267 : {
268 : // SUBROUTINE INFORMATION:
269 : // AUTHOR Simon Vidanovic
270 : // DATE WRITTEN June 2016
271 : // MODIFIED na
272 : // RE-ENGINEERED na
273 :
274 : // PURPOSE OF THIS SUBROUTINE:
275 : // Routine calculates outside air temperature for given surface.
276 : // Routine will return inside air temperature if it is interior surface. (refactoring from the code)
277 : //
278 : // NOTE: This routine has been copy/pasted in the past in several different modules with slight
279 : // modifications at some of those places. Exterior/interior surface air temperature is tied to surface.
280 8064 : Real64 temperature = 0;
281 :
282 8064 : if (ExtBoundCond > 0) // Interzone window
283 : {
284 0 : temperature = getInsideAirTemperature(state, t_SurfNum);
285 : } else {
286 8064 : if (ExtWind) {
287 : // Window is exposed to wind (and possibly rain)
288 8064 : if (state.dataEnvrn->IsRain) {
289 : // Raining: since wind exposed, outside window surface gets wet
290 0 : temperature = state.dataSurface->SurfOutWetBulbTemp(t_SurfNum);
291 : } else {
292 : // Dry
293 8064 : temperature = state.dataSurface->SurfOutDryBulbTemp(t_SurfNum);
294 : }
295 : } else {
296 : // Window not exposed to wind
297 0 : temperature = state.dataSurface->SurfOutDryBulbTemp(t_SurfNum);
298 : }
299 : }
300 :
301 8064 : return temperature;
302 : }
303 :
304 4032 : Real64 SurfaceData::getOutsideIR(EnergyPlusData &state, const int t_SurfNum) const
305 : {
306 : // SUBROUTINE INFORMATION:
307 : // AUTHOR Simon Vidanovic
308 : // DATE WRITTEN July 2016
309 : // MODIFIED na
310 : // RE-ENGINEERED na
311 :
312 : // PURPOSE OF THIS SUBROUTINE:
313 : // Calculates outside infrared radiation
314 4032 : Real64 value = 0;
315 4032 : if (ExtBoundCond > 0) {
316 0 : value = state.dataSurface->SurfWinIRfromParentZone(ExtBoundCond) + state.dataHeatBalSurf->SurfQdotRadHVACInPerArea(ExtBoundCond);
317 : } else {
318 4032 : Real64 tout = getOutsideAirTemperature(state, t_SurfNum) + Constant::Kelvin;
319 4032 : value = Constant::StefanBoltzmann * pow_4(tout);
320 4032 : value =
321 4032 : ViewFactorSkyIR * (state.dataSurface->SurfAirSkyRadSplit(t_SurfNum) * Constant::StefanBoltzmann * pow_4(state.dataEnvrn->SkyTempKelvin) +
322 4032 : (1.0 - state.dataSurface->SurfAirSkyRadSplit(t_SurfNum)) * value) +
323 4032 : ViewFactorGroundIR * value;
324 : }
325 4032 : return value;
326 : }
327 :
328 4032 : Real64 SurfaceData::getSWIncident(EnergyPlusData &state, const int t_SurfNum)
329 : {
330 : // SUBROUTINE INFORMATION:
331 : // AUTHOR Simon Vidanovic
332 : // DATE WRITTEN July 2016
333 : // MODIFIED na
334 : // RE-ENGINEERED na
335 :
336 : // PURPOSE OF THIS SUBROUTINE:
337 : // Return total short wave incident to the surface
338 :
339 4032 : return state.dataHeatBal->SurfQRadSWOutIncident(t_SurfNum) +
340 4032 : state.dataHeatBal->EnclSolQSWRad(state.dataSurface->Surface(t_SurfNum).SolarEnclIndex);
341 : }
342 :
343 10752 : int SurfaceData::getTotLayers(EnergyPlusData &state) const
344 : {
345 : // SUBROUTINE INFORMATION:
346 : // AUTHOR Simon Vidanovic
347 : // DATE WRITTEN August 2016
348 : // MODIFIED na
349 : // RE-ENGINEERED na
350 :
351 : // PURPOSE OF THIS SUBROUTINE:
352 : // Returns total number of layer for current surface
353 :
354 10752 : auto &construction(state.dataConstruction->Construct(Construction));
355 10752 : return construction.TotLayers;
356 : }
357 :
358 : // Computed Shape Category
359 43646 : ShapeCat SurfaceData::computed_shapeCat() const
360 : {
361 43646 : if (Shape == SurfaceShape::Triangle) {
362 197 : return ShapeCat::Triangular;
363 43449 : } else if (Shape == SurfaceShape::TriangularWindow) {
364 2 : return ShapeCat::Triangular;
365 43447 : } else if (Shape == SurfaceShape::TriangularDoor) {
366 0 : return ShapeCat::Triangular;
367 43447 : } else if (Shape == SurfaceShape::Rectangle) {
368 31314 : return ShapeCat::Rectangular;
369 12133 : } else if (Shape == SurfaceShape::RectangularDoorWindow) {
370 6694 : return ShapeCat::Rectangular;
371 5439 : } else if (Shape == SurfaceShape::RectangularOverhang) {
372 0 : return ShapeCat::Rectangular;
373 5439 : } else if (Shape == SurfaceShape::RectangularLeftFin) {
374 0 : return ShapeCat::Rectangular;
375 5439 : } else if (Shape == SurfaceShape::RectangularRightFin) {
376 0 : return ShapeCat::Rectangular;
377 5439 : } else if (IsConvex) {
378 5242 : return ShapeCat::Convex;
379 : } else {
380 197 : return ShapeCat::Nonconvex;
381 : }
382 : }
383 :
384 : // Computed Plane
385 87292 : SurfaceData::Plane SurfaceData::computed_plane() const
386 : {
387 87292 : Vertices::size_type const n(Vertex.size());
388 87292 : assert(n >= 3);
389 87292 : Vector center(0.0); // Center (vertex average) point (not mass centroid)
390 87292 : Real64 a(0.0), b(0.0), c(0.0), d(0.0); // Plane coefficients
391 438142 : for (Vertices::size_type i = 0; i < n; ++i) { // Newell's method for robustness (not speed)
392 350850 : Vector const &v(Vertex[i]);
393 350850 : Vector const &w(Vertex[(i + 1) % n]);
394 350850 : a += (v.y - w.y) * (v.z + w.z);
395 350850 : b += (v.z - w.z) * (v.x + w.x);
396 350850 : c += (v.x - w.x) * (v.y + w.y);
397 350850 : center += v;
398 : }
399 87292 : d = -(dot(center, Vector(a, b, c)) / n); // center/n is the center point
400 87292 : return Plane(a, b, c, d); // a*x + b*y + c*z + d = 0
401 87292 : }
402 :
403 : // Computed axis-projected 2D surface
404 43646 : Surface2D SurfaceData::computed_surface2d() const
405 : {
406 : // Project along axis of min surface range for 2D intersection use
407 43646 : Vertices::size_type const n(Vertex.size());
408 43646 : assert(n >= 3);
409 43646 : assert(plane == computed_plane()); // Set plane first
410 : using Vertex2D = ObjexxFCL::Vector2<Real64>;
411 : using Vertices2D = ObjexxFCL::Array1D<Vertex2D>;
412 :
413 : // Select axis to project along
414 43646 : Real64 const a(std::abs(plane.x)); // Plane normal x coordinate magnitude
415 43646 : Real64 const b(std::abs(plane.y)); // Plane normal y coordinate magnitude
416 43646 : Real64 const c(std::abs(plane.z)); // Plane normal z coordinate magnitude
417 43646 : 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
418 :
419 : // Set up 2D surface
420 43646 : Vertices2D v2d(n);
421 43646 : Vector const &v0(Vertex[0]);
422 43646 : if (axis == 0) { // Use y,z for 2D surface
423 13481 : Real64 yl(v0.y), yu(v0.y); // y coordinate ranges
424 13481 : Real64 zl(v0.z), zu(v0.z); // z coordinate ranges
425 67317 : for (Vertices::size_type i = 0; i < n; ++i) {
426 53836 : Vector const &v(Vertex[i]);
427 53836 : v2d[i] = Vertex2D(v.y, v.z);
428 53836 : yl = std::min(yl, v.y);
429 53836 : yu = std::max(yu, v.y);
430 53836 : zl = std::min(zl, v.z);
431 53836 : zu = std::max(zu, v.z);
432 : }
433 26962 : return Surface2D(shapeCat, axis, v2d, Vertex2D(yl, zl), Vertex2D(yu, zu));
434 30165 : } else if (axis == 1) { // Use x,z for 2D surface
435 15639 : Real64 xl(v0.x), xu(v0.x); // x coordinate ranges
436 15639 : Real64 zl(v0.z), zu(v0.z); // z coordinate ranges
437 78157 : for (Vertices::size_type i = 0; i < n; ++i) {
438 62518 : Vector const &v(Vertex[i]);
439 62518 : v2d[i] = Vertex2D(v.x, v.z);
440 62518 : xl = std::min(xl, v.x);
441 62518 : xu = std::max(xu, v.x);
442 62518 : zl = std::min(zl, v.z);
443 62518 : zu = std::max(zu, v.z);
444 : }
445 31278 : return Surface2D(shapeCat, axis, v2d, Vertex2D(xl, zl), Vertex2D(xu, zu));
446 : } else { // Use x,y for 2D surface
447 14526 : Real64 xl(v0.x), xu(v0.x); // x coordinate ranges
448 14526 : Real64 yl(v0.y), yu(v0.y); // y coordinate ranges
449 73597 : for (Vertices::size_type i = 0; i < n; ++i) {
450 59071 : Vector const &v(Vertex[i]);
451 59071 : v2d[i] = Vertex2D(v.x, v.y);
452 59071 : xl = std::min(xl, v.x);
453 59071 : xu = std::max(xu, v.x);
454 59071 : yl = std::min(yl, v.y);
455 59071 : yu = std::max(yu, v.y);
456 : }
457 29052 : return Surface2D(shapeCat, axis, v2d, Vertex2D(xl, yl), Vertex2D(xu, yu));
458 : }
459 43646 : }
460 :
461 15 : Real64 SurfaceData::get_average_height(EnergyPlusData &state) const
462 : {
463 15 : if (std::abs(SinTilt) < Constant::SmallDistance) {
464 0 : return 0.0;
465 : }
466 : using Vertex2D = ObjexxFCL::Vector2<Real64>;
467 : using Vertices2D = ObjexxFCL::Array1D<Vertex2D>;
468 15 : Vertices::size_type const n(Vertex.size());
469 15 : assert(n >= 3);
470 :
471 15 : Vertices2D v2d(n);
472 :
473 : // project onto 2D vertical plane
474 15 : Real64 xRef = Vertex[0].x;
475 15 : Real64 yRef = Vertex[0].y;
476 15 : Real64 const &saz(SinAzim);
477 15 : Real64 const &caz(CosAzim);
478 75 : for (Vertices::size_type i = 0; i < n; ++i) {
479 60 : Vector const &v(Vertex[i]);
480 60 : v2d[i] = Vertex2D(-(v.x - xRef) * caz + (v.y - yRef) * saz, v.z);
481 : }
482 :
483 : // piecewise linear integration
484 :
485 : // Get total width of polygon
486 15 : Real64 minX(v2d[0].x), maxX(v2d[0].x);
487 75 : for (Vertices::size_type i = 0; i < n; ++i) {
488 60 : Vertex2D const &v(v2d[i]);
489 60 : minX = std::min(minX, v.x);
490 60 : maxX = std::max(maxX, v.x);
491 : }
492 15 : Real64 totalWidth = maxX - minX;
493 :
494 15 : if (totalWidth == 0.0) {
495 : // This should never happen, but if it does, print a somewhat meaningful fatal error
496 : // (instead of allowing a divide by zero).
497 0 : ShowFatalError(state, format("Calculated projected surface width is zero for surface=\"{}\"", Name));
498 : }
499 :
500 15 : Real64 averageHeight = 0.0;
501 75 : for (Vertices::size_type i = 0; i < n; ++i) {
502 60 : Vertex2D const &v(v2d[i]);
503 :
504 : Vertex2D *v2;
505 60 : if (i == n - 1) {
506 15 : v2 = &v2d[0];
507 : } else {
508 45 : v2 = &v2d[i + 1];
509 : }
510 60 : averageHeight += 0.5 * (v.y + v2->y) * (v2->x - v.x) / totalWidth;
511 : }
512 15 : return std::abs(averageHeight) / SinTilt;
513 15 : }
514 :
515 579 : void SurfaceData::make_hash_key(EnergyPlusData &state, const int SurfNum)
516 : {
517 579 : calcHashKey = SurfaceCalcHashKey();
518 579 : calcHashKey.Construction = Construction;
519 579 : calcHashKey.Azimuth = round(Azimuth * 10.0) / 10.0;
520 579 : calcHashKey.Tilt = round(Tilt * 10.0) / 10.0;
521 579 : calcHashKey.Height = round(Height * 10.0) / 10.0;
522 579 : calcHashKey.Zone = Zone;
523 579 : calcHashKey.EnclIndex = SolarEnclIndex;
524 579 : calcHashKey.TAirRef = state.dataSurface->SurfTAirRef(SurfNum);
525 :
526 579 : int extBoundCond = state.dataSurface->Surface(SurfNum).ExtBoundCond;
527 579 : if (extBoundCond > 0) {
528 256 : calcHashKey.ExtZone = state.dataSurface->Surface(extBoundCond).Zone;
529 256 : calcHashKey.ExtEnclIndex = state.dataSurface->Surface(extBoundCond).SolarEnclIndex;
530 256 : calcHashKey.ExtCond = 1;
531 : } else {
532 323 : calcHashKey.ExtZone = 0;
533 323 : calcHashKey.ExtEnclIndex = 0;
534 323 : calcHashKey.ExtCond = extBoundCond;
535 : }
536 :
537 579 : calcHashKey.ExtSolar = ExtSolar;
538 579 : calcHashKey.ExtWind = ExtWind;
539 579 : calcHashKey.ViewFactorGround = round(ViewFactorGround * 10.0) / 10.0;
540 579 : calcHashKey.ViewFactorSky = round(ViewFactorSky * 10.0) / 10.0;
541 :
542 579 : calcHashKey.HeatTransferAlgorithm = HeatTransferAlgorithm;
543 579 : calcHashKey.intConvModel = state.dataSurface->surfIntConv(SurfNum).model;
544 579 : calcHashKey.extConvModel = state.dataSurface->surfExtConv(SurfNum).model;
545 579 : calcHashKey.intConvUserModelNum = state.dataSurface->surfIntConv(SurfNum).userModelNum;
546 579 : calcHashKey.extConvUserModelNum = state.dataSurface->surfExtConv(SurfNum).userModelNum;
547 579 : calcHashKey.OSCPtr = OSCPtr;
548 579 : calcHashKey.OSCMPtr = OSCMPtr;
549 :
550 579 : calcHashKey.FrameDivider = FrameDivider;
551 579 : calcHashKey.SurfWinStormWinConstr = state.dataSurface->SurfWinStormWinConstr(SurfNum);
552 :
553 579 : calcHashKey.MaterialMovInsulExt = state.dataSurface->SurfMaterialMovInsulExt(SurfNum);
554 579 : calcHashKey.MaterialMovInsulInt = state.dataSurface->SurfMaterialMovInsulInt(SurfNum);
555 579 : calcHashKey.SchedMovInsulExt = state.dataSurface->SurfSchedMovInsulExt(SurfNum);
556 579 : calcHashKey.SchedMovInsulInt = state.dataSurface->SurfSchedMovInsulInt(SurfNum);
557 579 : calcHashKey.ExternalShadingSchInd = state.dataSurface->Surface(SurfNum).SurfExternalShadingSchInd;
558 579 : calcHashKey.SurroundingSurfacesNum = state.dataSurface->Surface(SurfNum).SurfSurroundingSurfacesNum;
559 579 : calcHashKey.LinkedOutAirNode = state.dataSurface->Surface(SurfNum).SurfLinkedOutAirNode;
560 579 : calcHashKey.OutsideHeatSourceTermSchedule = OutsideHeatSourceTermSchedule;
561 579 : calcHashKey.InsideHeatSourceTermSchedule = InsideHeatSourceTermSchedule;
562 579 : calcHashKey.ViewFactorSrdSurfs = state.dataSurface->Surface(SurfNum).ViewFactorSrdSurfs;
563 579 : }
564 :
565 579 : void SurfaceData::set_representative_surface(EnergyPlusData &state, const int SurfNum)
566 : {
567 : // Make hash key for this surface (used to determine uniqueness)
568 579 : state.dataSurface->Surface(SurfNum).make_hash_key(state, SurfNum);
569 : // Insert surface key into map. If key already exists, it will not be added.
570 : // Assign the representative surface number based on the first instance of the identical key
571 579 : state.dataSurface->Surface(SurfNum).RepresentativeCalcSurfNum =
572 1158 : state.dataSurface->RepresentativeSurfaceMap.insert({state.dataSurface->Surface(SurfNum).calcHashKey, SurfNum}).first->second;
573 :
574 579 : state.dataSurface->Surface(state.dataSurface->Surface(SurfNum).RepresentativeCalcSurfNum).ConstituentSurfaceNums.push_back(SurfNum);
575 579 : }
576 :
577 : // Functions
578 :
579 2804678 : void SetSurfaceOutBulbTempAt(EnergyPlusData &state)
580 : {
581 2804678 : if (state.dataEnvrn->SiteTempGradient == 0.0) {
582 0 : for (int SurfNum = 1; SurfNum <= state.dataSurface->TotSurfaces; SurfNum++) {
583 0 : state.dataSurface->SurfOutDryBulbTemp(SurfNum) = state.dataEnvrn->OutDryBulbTemp;
584 0 : state.dataSurface->SurfOutWetBulbTemp(SurfNum) = state.dataEnvrn->OutWetBulbTemp;
585 : }
586 : } else {
587 2804678 : Real64 const BaseDryTemp(state.dataEnvrn->OutDryBulbTemp + state.dataEnvrn->WeatherFileTempModCoeff);
588 2804678 : Real64 const BaseWetTemp(state.dataEnvrn->OutWetBulbTemp + state.dataEnvrn->WeatherFileTempModCoeff);
589 179412538 : for (int SurfNum = 1; SurfNum <= state.dataSurface->TotSurfaces; SurfNum++) {
590 : // Base temperatures at Z = 0 (C)
591 176607860 : Real64 const Z(state.dataSurface->Surface(SurfNum).Centroid.z); // Centroid value
592 176607860 : if (Z <= 0.0) {
593 24211251 : state.dataSurface->SurfOutDryBulbTemp(SurfNum) = BaseDryTemp;
594 24211251 : state.dataSurface->SurfOutWetBulbTemp(SurfNum) = BaseWetTemp;
595 : } else {
596 152396609 : Real64 GradientDividend = state.dataEnvrn->SiteTempGradient * DataEnvironment::EarthRadius * Z;
597 152396609 : Real64 GradientDivisor = DataEnvironment::EarthRadius + Z;
598 152396609 : state.dataSurface->SurfOutDryBulbTemp(SurfNum) = BaseDryTemp - GradientDividend / GradientDivisor;
599 152396609 : state.dataSurface->SurfOutWetBulbTemp(SurfNum) = BaseWetTemp - GradientDividend / GradientDivisor;
600 : }
601 : }
602 : }
603 2804678 : }
604 :
605 2804678 : void CheckSurfaceOutBulbTempAt(EnergyPlusData &state)
606 : {
607 : // Using/Aliasing
608 : using DataEnvironment::SetOutBulbTempAt_error;
609 :
610 2804678 : Real64 minBulb = 0.0;
611 179412538 : for (int SurfNum = 1; SurfNum <= state.dataSurface->TotSurfaces; SurfNum++) {
612 176607860 : minBulb = min(minBulb, state.dataSurface->SurfOutDryBulbTemp(SurfNum), state.dataSurface->SurfOutWetBulbTemp(SurfNum));
613 176607860 : if (minBulb < -100.0)
614 0 : SetOutBulbTempAt_error(state, "Surface", state.dataSurface->Surface(SurfNum).Centroid.z, state.dataSurface->Surface(SurfNum).Name);
615 : }
616 2804678 : }
617 :
618 2804678 : void SetSurfaceWindSpeedAt(EnergyPlusData &state)
619 : {
620 2804678 : Real64 const fac(state.dataEnvrn->WindSpeed * state.dataEnvrn->WeatherFileWindModCoeff *
621 2804678 : std::pow(state.dataEnvrn->SiteWindBLHeight, -state.dataEnvrn->SiteWindExp));
622 2804678 : if (state.dataEnvrn->SiteWindExp == 0.0) {
623 0 : for (int SurfNum = 1; SurfNum <= state.dataSurface->TotSurfaces; SurfNum++) {
624 0 : state.dataSurface->SurfOutWindSpeed(SurfNum) = state.dataEnvrn->WindSpeed;
625 : }
626 : } else {
627 :
628 179412538 : for (int SurfNum = 1; SurfNum <= state.dataSurface->TotSurfaces; SurfNum++) {
629 176607860 : if (!state.dataSurface->Surface(SurfNum).ExtWind) continue;
630 69659398 : Real64 const Z(state.dataSurface->Surface(SurfNum).Centroid.z); // Centroid value
631 69659398 : if (Z <= 0.0) {
632 72555 : state.dataSurface->SurfOutWindSpeed(SurfNum) = 0.0;
633 : } else {
634 : // [Met] - at meterological Station, Height of measurement is usually 10m above ground
635 : // LocalWindSpeed = Windspeed [Met] * (Wind Boundary LayerThickness [Met]/Height [Met])**Wind Exponent[Met] &
636 : // * (Height above ground / Site Wind Boundary Layer Thickness) ** Site Wind Exponent
637 69586843 : state.dataSurface->SurfOutWindSpeed(SurfNum) = fac * std::pow(Z, state.dataEnvrn->SiteWindExp);
638 : }
639 : }
640 : }
641 2804678 : }
642 :
643 2804678 : void SetSurfaceWindDirAt(EnergyPlusData &state)
644 : {
645 179412538 : for (int SurfNum = 1; SurfNum <= state.dataSurface->TotSurfaces; SurfNum++) {
646 176607860 : state.dataSurface->SurfOutWindDir(SurfNum) = state.dataEnvrn->WindDir;
647 : }
648 2804678 : }
649 :
650 40503 : std::string cSurfaceClass(SurfaceClass const ClassNo)
651 : {
652 :
653 : // FUNCTION INFORMATION:
654 : // AUTHOR Linda Lawrie
655 : // DATE WRITTEN May 2006
656 : // MODIFIED na
657 : // RE-ENGINEERED na
658 :
659 : // PURPOSE OF THIS FUNCTION:
660 : // This function returns a string based on class number.
661 :
662 : // Return value
663 40503 : std::string ClassName;
664 :
665 40503 : switch (ClassNo) {
666 19248 : case SurfaceClass::Wall: {
667 19248 : ClassName = "Wall";
668 19248 : } break;
669 6010 : case SurfaceClass::Floor: {
670 6010 : ClassName = "Floor";
671 6010 : } break;
672 5119 : case SurfaceClass::Roof: {
673 5119 : ClassName = "Roof";
674 5119 : } break;
675 5716 : case SurfaceClass::Window: {
676 5716 : ClassName = "Window";
677 5716 : } break;
678 0 : case SurfaceClass::GlassDoor: {
679 0 : ClassName = "Glass Door";
680 0 : } break;
681 424 : case SurfaceClass::Door: {
682 424 : ClassName = "Door";
683 424 : } break;
684 4 : case SurfaceClass::TDD_Dome: {
685 4 : ClassName = "TubularDaylightDome";
686 4 : } break;
687 0 : case SurfaceClass::TDD_Diffuser: {
688 0 : ClassName = "TubularDaylightDiffuser";
689 0 : } break;
690 3314 : case SurfaceClass::IntMass: {
691 3314 : ClassName = "Internal Mass";
692 3314 : } break;
693 580 : case SurfaceClass::Shading: {
694 580 : ClassName = "Shading";
695 580 : } break;
696 34 : case SurfaceClass::Detached_B: {
697 34 : ClassName = "Detached Shading:Building";
698 34 : } break;
699 54 : case SurfaceClass::Detached_F: {
700 54 : ClassName = "Detached Shading:Fixed";
701 54 : } break;
702 0 : default: {
703 0 : ClassName = "Invalid/Unknown";
704 0 : } break;
705 : }
706 :
707 40503 : return ClassName;
708 0 : }
709 186115 : Real64 AbsFrontSide(EnergyPlusData &state, int SurfNum)
710 : {
711 : Real64 AbsorptanceFromExteriorFrontSide =
712 186115 : (state.dataSurface->SurfWinExtBeamAbsByShade(SurfNum) + state.dataSurface->SurfWinExtDiffAbsByShade(SurfNum)) *
713 186115 : state.dataSurface->SurfWinShadeAbsFacFace1(SurfNum);
714 : Real64 AbsorptanceFromInteriorFrontSide =
715 186115 : (state.dataSurface->SurfWinIntBeamAbsByShade(SurfNum) + state.dataSurface->SurfWinIntSWAbsByShade(SurfNum)) *
716 186115 : state.dataSurface->SurfWinShadeAbsFacFace2(SurfNum);
717 186115 : return AbsorptanceFromExteriorFrontSide + AbsorptanceFromInteriorFrontSide;
718 : }
719 :
720 186115 : Real64 AbsBackSide(EnergyPlusData &state, int SurfNum)
721 : {
722 : Real64 AbsorptanceFromInteriorBackSide =
723 186115 : (state.dataSurface->SurfWinIntBeamAbsByShade(SurfNum) + state.dataSurface->SurfWinIntSWAbsByShade(SurfNum)) *
724 186115 : state.dataSurface->SurfWinShadeAbsFacFace1(SurfNum);
725 : Real64 AbsorptanceFromExteriorBackSide =
726 186115 : (state.dataSurface->SurfWinExtBeamAbsByShade(SurfNum) + state.dataSurface->SurfWinExtDiffAbsByShade(SurfNum)) *
727 186115 : state.dataSurface->SurfWinShadeAbsFacFace2(SurfNum);
728 186115 : return AbsorptanceFromExteriorBackSide + AbsorptanceFromInteriorBackSide;
729 : }
730 :
731 796 : void GetVariableAbsorptanceSurfaceList(EnergyPlusData &state)
732 : {
733 796 : if (!state.dataHeatBal->AnyVariableAbsorptance) return;
734 7 : for (int surfNum : state.dataSurface->AllHTSurfaceList) {
735 6 : auto const &thisSurface = state.dataSurface->Surface(surfNum);
736 6 : int ConstrNum = thisSurface.Construction;
737 6 : auto const &thisConstruct = state.dataConstruction->Construct(ConstrNum);
738 6 : int TotLayers = thisConstruct.TotLayers;
739 6 : if (TotLayers == 0) continue;
740 6 : int materNum = thisConstruct.LayerPoint(1);
741 6 : if (materNum == 0) continue; // error finding material number
742 6 : auto const *thisMaterial = dynamic_cast<const Material::MaterialChild *>(state.dataMaterial->Material(materNum));
743 6 : assert(thisMaterial != nullptr);
744 6 : if (thisMaterial->absorpVarCtrlSignal != Material::VariableAbsCtrlSignal::Invalid) {
745 : // check for dynamic coating defined on interior surface
746 1 : if (thisSurface.ExtBoundCond != ExternalEnvironment) {
747 0 : ShowWarningError(state,
748 0 : format("MaterialProperty:VariableAbsorptance defined on an interior surface, {}. This VariableAbsorptance property "
749 : "will be ignored here",
750 0 : thisSurface.Name));
751 : } else {
752 1 : state.dataSurface->AllVaryAbsOpaqSurfaceList.push_back(surfNum);
753 : }
754 : }
755 1 : }
756 : // check for dynamic coating defined on the non-outside layer of a construction
757 5 : for (int ConstrNum = 1; ConstrNum <= state.dataHeatBal->TotConstructs; ++ConstrNum) {
758 4 : auto const &thisConstruct = state.dataConstruction->Construct(ConstrNum);
759 4 : for (int Layer = 2; Layer <= thisConstruct.TotLayers; ++Layer) {
760 0 : auto const *thisMaterial = dynamic_cast<const Material::MaterialChild *>(state.dataMaterial->Material(thisConstruct.LayerPoint(Layer)));
761 0 : if (thisMaterial->absorpVarCtrlSignal != Material::VariableAbsCtrlSignal::Invalid) {
762 0 : ShowWarningError(state,
763 0 : format("MaterialProperty:VariableAbsorptance defined on a inside-layer materials, {}. This VariableAbsorptance "
764 : "property will be ignored here",
765 0 : thisMaterial->Name));
766 : }
767 : }
768 : }
769 : }
770 :
771 0 : Compass4 AzimuthToCompass4(Real64 azimuth)
772 : {
773 0 : assert(azimuth >= 0.0 && azimuth < 360.0);
774 0 : for (int c4 = 0; c4 < static_cast<int>(Compass4::Num); ++c4) {
775 0 : Real64 lo = Compass4AzimuthLo[c4];
776 0 : Real64 hi = Compass4AzimuthHi[c4];
777 0 : if (lo > hi) {
778 0 : if (azimuth >= lo || azimuth < hi) return static_cast<Compass4>(c4);
779 : } else {
780 0 : if (azimuth >= lo && azimuth < hi) return static_cast<Compass4>(c4);
781 : }
782 : }
783 0 : assert(false);
784 : return Compass4::Invalid;
785 : }
786 :
787 30206 : Compass8 AzimuthToCompass8(Real64 azimuth)
788 : {
789 30206 : assert(azimuth >= 0.0 && azimuth < 360.0);
790 125704 : for (int c8 = 0; c8 < static_cast<int>(Compass8::Num); ++c8) {
791 125704 : Real64 lo = Compass8AzimuthLo[c8];
792 125704 : Real64 hi = Compass8AzimuthHi[c8];
793 125704 : if (lo > hi) {
794 30206 : if (azimuth >= lo || azimuth < hi) return static_cast<Compass8>(c8);
795 : } else {
796 95498 : if (azimuth >= lo && azimuth < hi) return static_cast<Compass8>(c8);
797 : }
798 : }
799 0 : assert(false);
800 : return Compass8::Invalid;
801 : }
802 :
803 : } // namespace EnergyPlus::DataSurfaces
|