Line data Source code
1 : // EnergyPlus, Copyright (c) 1996-2025, The Board of Trustees of the University of Illinois,
2 : // The Regents of the University of California, through Lawrence Berkeley National Laboratory
3 : // (subject to receipt of any required approvals from the U.S. Dept. of Energy), Oak Ridge
4 : // National Laboratory, managed by UT-Battelle, Alliance for Sustainable Energy, LLC, and other
5 : // contributors. All rights reserved.
6 : //
7 : // NOTICE: This Software was developed under funding from the U.S. Department of Energy and the
8 : // U.S. Government consequently retains certain rights. As such, the U.S. Government has been
9 : // granted for itself and others acting on its behalf a paid-up, nonexclusive, irrevocable,
10 : // worldwide license in the Software to reproduce, distribute copies to the public, prepare
11 : // derivative works, and perform publicly and display publicly, and to permit others to do so.
12 : //
13 : // Redistribution and use in source and binary forms, with or without modification, are permitted
14 : // provided that the following conditions are met:
15 : //
16 : // (1) Redistributions of source code must retain the above copyright notice, this list of
17 : // conditions and the following disclaimer.
18 : //
19 : // (2) Redistributions in binary form must reproduce the above copyright notice, this list of
20 : // conditions and the following disclaimer in the documentation and/or other materials
21 : // provided with the distribution.
22 : //
23 : // (3) Neither the name of the University of California, Lawrence Berkeley National Laboratory,
24 : // the University of Illinois, U.S. Dept. of Energy nor the names of its contributors may be
25 : // used to endorse or promote products derived from this software without specific prior
26 : // written permission.
27 : //
28 : // (4) Use of EnergyPlus(TM) Name. If Licensee (i) distributes the software in stand-alone form
29 : // without changes from the version obtained under this License, or (ii) Licensee makes a
30 : // reference solely to the software portion of its product, Licensee must refer to the
31 : // software as "EnergyPlus version X" software, where "X" is the version number Licensee
32 : // obtained under this License and may not use a different name for the software. Except as
33 : // specifically required in this Section (4), Licensee shall not use in a company name, a
34 : // product name, in advertising, publicity, or other promotional activities any name, trade
35 : // name, trademark, logo, or other designation of "EnergyPlus", "E+", "e+" or confusingly
36 : // similar designation, without the U.S. Department of Energy's prior written consent.
37 : //
38 : // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
39 : // IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
40 : // AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
41 : // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
42 : // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
43 : // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
44 : // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
45 : // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
46 : // POSSIBILITY OF SUCH DAMAGE.
47 :
48 : // C++ Headers
49 : #include <algorithm>
50 : #include <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 44753 : Surface2D::Surface2D(ShapeCat const shapeCat, int const axis, Vertices const &v, Vector2D const &vl, Vector2D const &vu)
91 44753 : : axis(axis), vertices(v), vl(vl), vu(vu)
92 : {
93 44753 : size_type const n(vertices.size());
94 44753 : 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 44753 : Real64 area(0.0); // Actually 2x the signed area
99 224606 : for (Vertices::size_type i = 0; i < n; ++i) {
100 179853 : Vector2D const &v(vertices[i]);
101 179853 : Vector2D const &w(vertices[(i + 1) % n]);
102 179853 : area += (v.x * w.y) - (w.x * v.y);
103 : }
104 44753 : if (area < 0.0) {
105 22364 : std::reverse(vertices.begin() + 1, vertices.end()); // Vertices in clockwise order: Reverse all but first
106 : }
107 :
108 : // Set up edge vectors for ray--surface intersection tests
109 44753 : edges.reserve(n);
110 224606 : for (Vertices::size_type i = 0; i < n; ++i) {
111 179853 : edges.push_back(vertices[(i + 1) % n] - vertices[i]);
112 : }
113 44753 : if (shapeCat == ShapeCat::Rectangular) { // Set side length squared for ray--surface intersection tests
114 39067 : assert(n == 4u);
115 39067 : s1 = edges[0].magnitude_squared();
116 39067 : s3 = edges[3].magnitude_squared();
117 5686 : } else if ((shapeCat == ShapeCat::Nonconvex) || (n >= nVerticesBig)) { // Set up slabs
118 197 : assert(n >= 4u);
119 197 : slabYs.reserve(n);
120 2025 : for (size_type i = 0; i < n; ++i) {
121 1828 : slabYs.push_back(vertices[i].y);
122 : }
123 197 : std::sort(slabYs.begin(), slabYs.end()); // Sort the vertex y coordinates
124 197 : auto const iClip(std::unique(slabYs.begin(), slabYs.end())); // Remove duplicate y-coordinate elements
125 197 : slabYs.erase(iClip, slabYs.end());
126 197 : slabYs.shrink_to_fit();
127 884 : for (size_type iSlab = 0, iSlab_end = slabYs.size() - 1; iSlab < iSlab_end; ++iSlab) { // Create slabs
128 687 : Real64 xl(std::numeric_limits<Real64>::max());
129 687 : Real64 xu(std::numeric_limits<Real64>::lowest());
130 687 : Real64 const yl(slabYs[iSlab]);
131 687 : Real64 const yu(slabYs[iSlab + 1]);
132 687 : slabs.push_back(Slab(yl, yu));
133 687 : Slab &slab(slabs.back());
134 : using CrossEdge = std::tuple<Real64, Real64, size_type>;
135 : using CrossEdges = std::vector<CrossEdge>;
136 687 : CrossEdges crossEdges;
137 8073 : for (size_type i = 0; i < n; ++i) { // Find edges crossing slab
138 7386 : Vector2D const &v(vertices[i]);
139 7386 : Vector2D const &w(vertices[(i + 1) % n]);
140 7386 : if (((v.y <= yl) && (yu <= w.y)) || // Crosses upward
141 6410 : ((yu <= v.y) && (w.y <= yl))) // Crosses downward
142 : {
143 1952 : Edge const &e(edges[i]);
144 1952 : assert(e.y != 0.0);
145 1952 : Real64 const exy(e.x / e.y);
146 1952 : Real64 const xb(v.x + (yl - v.y) * exy); // x_bot coordinate where edge intersects yl
147 1952 : Real64 const xt(v.x + (yu - v.y) * exy); // x_top coordinate where edge intersects yu
148 1952 : xl = std::min(xl, std::min(xb, xt));
149 1952 : xu = std::max(xu, std::max(xb, xt));
150 1952 : crossEdges.push_back(std::make_tuple(xb, xt, i));
151 : }
152 : }
153 687 : slab.xl = xl;
154 687 : slab.xu = xu;
155 687 : assert(crossEdges.size() >= 2u);
156 687 : std::sort(crossEdges.begin(),
157 1374 : crossEdges.end(),
158 2724 : [](CrossEdge const &e1, CrossEdge const &e2) -> bool // Lambda to sort by x_mid
159 : {
160 2724 : return std::get<0>(e1) + std::get<1>(e1) <
161 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
162 : });
163 : #ifndef NDEBUG // Check x_bot and x_top are also sorted
164 687 : Real64 xb(std::get<0>(crossEdges[0]));
165 687 : Real64 xt(std::get<1>(crossEdges[0]));
166 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
167 2639 : for (auto const &edge : crossEdges) { // Detect non-simple polygon with crossing edges
168 1952 : Real64 const xbe(std::get<0>(edge));
169 1952 : Real64 const xte(std::get<1>(edge));
170 1952 : assert(xb <= xbe + tol);
171 1952 : assert(xt <= xte + tol);
172 1952 : xb = xbe;
173 1952 : xt = xte;
174 687 : }
175 : #endif
176 687 : assert((shapeCat == ShapeCat::Nonconvex) || (crossEdges.size() == 2u));
177 2639 : for (auto const &edge : crossEdges) {
178 1952 : size_type const iEdge(std::get<2>(edge));
179 1952 : slab.edges.push_back(iEdge); // Add edge to slab
180 1952 : Vector2D const &e(edges[iEdge]);
181 1952 : assert(e.y != 0.0); // Constant y edge can't be a crossing edge
182 1952 : slab.edgesXY.push_back(e.y != 0.0 ? e.x / e.y : 0.0); // Edge inverse slope
183 687 : }
184 687 : assert(slab.edges.size() % 2 == 0u);
185 687 : assert(slab.edges.size() == slab.edgesXY.size());
186 687 : }
187 197 : }
188 44753 : }
189 :
190 : // Set Precomputed Parameters
191 47290 : void SurfaceData::set_computed_geometry()
192 : {
193 47290 : if (Vertex.size() >= 3) { // Skip no-vertex "surfaces"
194 44753 : shapeCat = computed_shapeCat();
195 44753 : plane = computed_plane();
196 44753 : surface2d = computed_surface2d();
197 : }
198 47290 : }
199 :
200 487901982 : Real64 SurfaceData::getInsideAirTemperature(EnergyPlusData &state, const int t_SurfNum) const
201 : {
202 : // SUBROUTINE INFORMATION:
203 : // AUTHOR Simon Vidanovic
204 : // DATE WRITTEN June 2016
205 : // MODIFIED na
206 : // RE-ENGINEERED na
207 :
208 : // PURPOSE OF THIS SUBROUTINE:
209 : // Routine calculates reference air temperature for given surface (refactoring from the code)
210 : //
211 : // NOTE: This routine has been copy/pasted in the past in several different modules with slight
212 : // modifications at some of those places. It is quite logical that reference air temperature
213 : // for the surface is calculated as public function of SurfaceData structure (class) and is
214 : // later called as needed. Note that SurfaceNum had to be passed to this routine because of
215 : // access to global array SurfTempEffBulkAir. I would propose refactoring where SurfTempEffBulkAir
216 : // is part of SurfaceData structure and instead of calling SurfTempEffBulkAir( SurfNum ) it should
217 : // be called Surface( SurfNum ).TempEffBulkAir (Simon Vidanovic)
218 :
219 487901982 : Real64 RefAirTemp = 0;
220 :
221 : // determine reference air temperature for this surface
222 487901982 : auto &thisSpaceHB = state.dataZoneTempPredictorCorrector->spaceHeatBalance(this->spaceNum);
223 487901982 : switch (state.dataSurface->SurfTAirRef(t_SurfNum)) {
224 2522441 : case RefAirTemp::ZoneMeanAirTemp: {
225 2522441 : RefAirTemp = thisSpaceHB.MAT;
226 2522441 : } break;
227 4057129 : case RefAirTemp::AdjacentAirTemp: {
228 4057129 : RefAirTemp = state.dataHeatBal->SurfTempEffBulkAir(t_SurfNum);
229 4057129 : } break;
230 0 : case RefAirTemp::ZoneSupplyAirTemp: {
231 : // determine ZoneEquipConfigNum for this zone
232 : // ControlledZoneAirFlag = .FALSE.
233 : // ZoneEquipConfigNum = ZoneNum;
234 : // check whether this zone is a controlled zone or not
235 0 : if (!state.dataHeatBal->Zone(Zone).IsControlled) {
236 0 : ShowFatalError(state,
237 0 : format("Zones must be controlled for Ceiling-Diffuser Convection model. No system serves zone {}",
238 0 : state.dataHeatBal->Zone(Zone).Name));
239 : // return;
240 : }
241 : // determine supply air conditions
242 0 : Real64 SumSysMCp = 0;
243 0 : Real64 SumSysMCpT = 0;
244 0 : auto const &inletNodes = (state.dataHeatBal->doSpaceHeatBalance) ? state.dataZoneEquip->spaceEquipConfig(this->spaceNum).InletNode
245 0 : : state.dataZoneEquip->ZoneEquipConfig(Zone).InletNode;
246 0 : for (int nodeNum : inletNodes) {
247 0 : auto const &inNode = state.dataLoopNodes->Node(nodeNum);
248 0 : Real64 CpAir = PsyCpAirFnW(thisSpaceHB.airHumRat);
249 0 : SumSysMCp += inNode.MassFlowRate * CpAir;
250 0 : SumSysMCpT += inNode.MassFlowRate * CpAir * inNode.Temp;
251 : }
252 : // a weighted average of the inlet temperatures.
253 0 : if (SumSysMCp > 0.0) {
254 : // a weighted average of the inlet temperatures.
255 0 : RefAirTemp = SumSysMCpT / SumSysMCp;
256 : } else {
257 0 : RefAirTemp = thisSpaceHB.MAT;
258 : }
259 0 : } break;
260 481322412 : default: {
261 : // currently set to mean air temp but should add error warning here
262 481322412 : RefAirTemp = thisSpaceHB.MAT;
263 481322412 : } break;
264 : }
265 :
266 487901982 : return RefAirTemp;
267 : }
268 :
269 8064 : Real64 SurfaceData::getOutsideAirTemperature(EnergyPlusData &state, const int t_SurfNum) const
270 : {
271 : // SUBROUTINE INFORMATION:
272 : // AUTHOR Simon Vidanovic
273 : // DATE WRITTEN June 2016
274 : // MODIFIED na
275 : // RE-ENGINEERED na
276 :
277 : // PURPOSE OF THIS SUBROUTINE:
278 : // Routine calculates outside air temperature for given surface.
279 : // Routine will return inside air temperature if it is interior surface. (refactoring from the code)
280 : //
281 : // NOTE: This routine has been copy/pasted in the past in several different modules with slight
282 : // modifications at some of those places. Exterior/interior surface air temperature is tied to surface.
283 8064 : Real64 temperature = 0;
284 :
285 8064 : if (ExtBoundCond > 0) // Interzone window
286 : {
287 0 : temperature = getInsideAirTemperature(state, t_SurfNum);
288 : } else {
289 8064 : if (ExtWind) {
290 : // Window is exposed to wind (and possibly rain)
291 8064 : if (state.dataEnvrn->IsRain) {
292 : // Raining: since wind exposed, outside window surface gets wet
293 0 : temperature = state.dataSurface->SurfOutWetBulbTemp(t_SurfNum);
294 : } else {
295 : // Dry
296 8064 : temperature = state.dataSurface->SurfOutDryBulbTemp(t_SurfNum);
297 : }
298 : } else {
299 : // Window not exposed to wind
300 0 : temperature = state.dataSurface->SurfOutDryBulbTemp(t_SurfNum);
301 : }
302 : }
303 :
304 8064 : return temperature;
305 : }
306 :
307 4032 : Real64 SurfaceData::getOutsideIR(EnergyPlusData &state, const int t_SurfNum) const
308 : {
309 : // SUBROUTINE INFORMATION:
310 : // AUTHOR Simon Vidanovic
311 : // DATE WRITTEN July 2016
312 : // MODIFIED na
313 : // RE-ENGINEERED na
314 :
315 : // PURPOSE OF THIS SUBROUTINE:
316 : // Calculates outside infrared radiation
317 4032 : Real64 value = 0;
318 4032 : if (ExtBoundCond > 0) {
319 0 : value = state.dataSurface->SurfWinIRfromParentZone(ExtBoundCond) + state.dataHeatBalSurf->SurfQdotRadHVACInPerArea(ExtBoundCond);
320 : } else {
321 4032 : Real64 tout = getOutsideAirTemperature(state, t_SurfNum) + Constant::Kelvin;
322 4032 : value = Constant::StefanBoltzmann * pow_4(tout);
323 4032 : value =
324 4032 : ViewFactorSkyIR * (state.dataSurface->SurfAirSkyRadSplit(t_SurfNum) * Constant::StefanBoltzmann * pow_4(state.dataEnvrn->SkyTempKelvin) +
325 4032 : (1.0 - state.dataSurface->SurfAirSkyRadSplit(t_SurfNum)) * value) +
326 4032 : ViewFactorGroundIR * value;
327 : }
328 4032 : return value;
329 : }
330 :
331 4032 : Real64 SurfaceData::getSWIncident(EnergyPlusData &state, const int t_SurfNum)
332 : {
333 : // SUBROUTINE INFORMATION:
334 : // AUTHOR Simon Vidanovic
335 : // DATE WRITTEN July 2016
336 : // MODIFIED na
337 : // RE-ENGINEERED na
338 :
339 : // PURPOSE OF THIS SUBROUTINE:
340 : // Return total short wave incident to the surface
341 :
342 4032 : return state.dataHeatBal->SurfQRadSWOutIncident(t_SurfNum) +
343 4032 : state.dataHeatBal->EnclSolQSWRad(state.dataSurface->Surface(t_SurfNum).SolarEnclIndex);
344 : }
345 :
346 10752 : int SurfaceData::getTotLayers(EnergyPlusData &state) const
347 : {
348 : // SUBROUTINE INFORMATION:
349 : // AUTHOR Simon Vidanovic
350 : // DATE WRITTEN August 2016
351 : // MODIFIED na
352 : // RE-ENGINEERED na
353 :
354 : // PURPOSE OF THIS SUBROUTINE:
355 : // Returns total number of layer for current surface
356 :
357 10752 : auto &construction(state.dataConstruction->Construct(Construction));
358 10752 : return construction.TotLayers;
359 : }
360 :
361 : // Computed Shape Category
362 44753 : ShapeCat SurfaceData::computed_shapeCat() const
363 : {
364 44753 : if (Shape == SurfaceShape::Triangle) {
365 197 : return ShapeCat::Triangular;
366 44556 : } else if (Shape == SurfaceShape::TriangularWindow) {
367 2 : return ShapeCat::Triangular;
368 44554 : } else if (Shape == SurfaceShape::TriangularDoor) {
369 0 : return ShapeCat::Triangular;
370 44554 : } else if (Shape == SurfaceShape::Rectangle) {
371 32229 : return ShapeCat::Rectangular;
372 12325 : } else if (Shape == SurfaceShape::RectangularDoorWindow) {
373 6838 : return ShapeCat::Rectangular;
374 5487 : } else if (Shape == SurfaceShape::RectangularOverhang) {
375 0 : return ShapeCat::Rectangular;
376 5487 : } else if (Shape == SurfaceShape::RectangularLeftFin) {
377 0 : return ShapeCat::Rectangular;
378 5487 : } else if (Shape == SurfaceShape::RectangularRightFin) {
379 0 : return ShapeCat::Rectangular;
380 5487 : } else if (IsConvex) {
381 5290 : return ShapeCat::Convex;
382 : } else {
383 197 : return ShapeCat::Nonconvex;
384 : }
385 : }
386 :
387 : // Computed Plane
388 89506 : SurfaceData::Plane SurfaceData::computed_plane() const
389 : {
390 89506 : Vertices::size_type const n(Vertex.size());
391 89506 : assert(n >= 3);
392 89506 : Vector center(0.0); // Center (vertex average) point (not mass centroid)
393 89506 : Real64 a(0.0), b(0.0), c(0.0), d(0.0); // Plane coefficients
394 449212 : for (Vertices::size_type i = 0; i < n; ++i) { // Newell's method for robustness (not speed)
395 359706 : Vector const &v(Vertex[i]);
396 359706 : Vector const &w(Vertex[(i + 1) % n]);
397 359706 : a += (v.y - w.y) * (v.z + w.z);
398 359706 : b += (v.z - w.z) * (v.x + w.x);
399 359706 : c += (v.x - w.x) * (v.y + w.y);
400 359706 : center += v;
401 : }
402 89506 : d = -(dot(center, Vector(a, b, c)) / n); // center/n is the center point
403 89506 : return Plane(a, b, c, d); // a*x + b*y + c*z + d = 0
404 89506 : }
405 :
406 : // Computed axis-projected 2D surface
407 44753 : Surface2D SurfaceData::computed_surface2d() const
408 : {
409 : // Project along axis of min surface range for 2D intersection use
410 44753 : Vertices::size_type const n(Vertex.size());
411 44753 : assert(n >= 3);
412 44753 : assert(plane == computed_plane()); // Set plane first
413 : using Vertex2D = ObjexxFCL::Vector2<Real64>;
414 : using Vertices2D = ObjexxFCL::Array1D<Vertex2D>;
415 :
416 : // Select axis to project along
417 44753 : Real64 const a(std::abs(plane.x)); // Plane normal x coordinate magnitude
418 44753 : Real64 const b(std::abs(plane.y)); // Plane normal y coordinate magnitude
419 44753 : Real64 const c(std::abs(plane.z)); // Plane normal z coordinate magnitude
420 44753 : 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
421 :
422 : // Set up 2D surface
423 44753 : Vertices2D v2d(n);
424 44753 : Vector const &v0(Vertex[0]);
425 44753 : if (axis == 0) { // Use y,z for 2D surface
426 13855 : Real64 yl(v0.y), yu(v0.y); // y coordinate ranges
427 13855 : Real64 zl(v0.z), zu(v0.z); // z coordinate ranges
428 69187 : for (Vertices::size_type i = 0; i < n; ++i) {
429 55332 : Vector const &v(Vertex[i]);
430 55332 : v2d[i] = Vertex2D(v.y, v.z);
431 55332 : yl = std::min(yl, v.y);
432 55332 : yu = std::max(yu, v.y);
433 55332 : zl = std::min(zl, v.z);
434 55332 : zu = std::max(zu, v.z);
435 : }
436 13855 : return Surface2D(shapeCat, axis, v2d, Vertex2D(yl, zl), Vertex2D(yu, zu));
437 30898 : } else if (axis == 1) { // Use x,z for 2D surface
438 16011 : Real64 xl(v0.x), xu(v0.x); // x coordinate ranges
439 16011 : Real64 zl(v0.z), zu(v0.z); // z coordinate ranges
440 80017 : for (Vertices::size_type i = 0; i < n; ++i) {
441 64006 : Vector const &v(Vertex[i]);
442 64006 : v2d[i] = Vertex2D(v.x, v.z);
443 64006 : xl = std::min(xl, v.x);
444 64006 : xu = std::max(xu, v.x);
445 64006 : zl = std::min(zl, v.z);
446 64006 : zu = std::max(zu, v.z);
447 : }
448 16011 : return Surface2D(shapeCat, axis, v2d, Vertex2D(xl, zl), Vertex2D(xu, zu));
449 : } else { // Use x,y for 2D surface
450 14887 : Real64 xl(v0.x), xu(v0.x); // x coordinate ranges
451 14887 : Real64 yl(v0.y), yu(v0.y); // y coordinate ranges
452 75402 : for (Vertices::size_type i = 0; i < n; ++i) {
453 60515 : Vector const &v(Vertex[i]);
454 60515 : v2d[i] = Vertex2D(v.x, v.y);
455 60515 : xl = std::min(xl, v.x);
456 60515 : xu = std::max(xu, v.x);
457 60515 : yl = std::min(yl, v.y);
458 60515 : yu = std::max(yu, v.y);
459 : }
460 14887 : return Surface2D(shapeCat, axis, v2d, Vertex2D(xl, yl), Vertex2D(xu, yu));
461 : }
462 44753 : }
463 :
464 16 : Real64 SurfaceData::get_average_height(EnergyPlusData &state) const
465 : {
466 16 : if (std::abs(SinTilt) < Constant::SmallDistance) {
467 0 : return 0.0;
468 : }
469 : using Vertex2D = ObjexxFCL::Vector2<Real64>;
470 : using Vertices2D = ObjexxFCL::Array1D<Vertex2D>;
471 16 : Vertices::size_type const n(Vertex.size());
472 16 : assert(n >= 3);
473 :
474 16 : Vertices2D v2d(n);
475 :
476 : // project onto 2D vertical plane
477 16 : Real64 xRef = Vertex[0].x;
478 16 : Real64 yRef = Vertex[0].y;
479 16 : Real64 const &saz(SinAzim);
480 16 : Real64 const &caz(CosAzim);
481 80 : for (Vertices::size_type i = 0; i < n; ++i) {
482 64 : Vector const &v(Vertex[i]);
483 64 : v2d[i] = Vertex2D(-(v.x - xRef) * caz + (v.y - yRef) * saz, v.z);
484 : }
485 :
486 : // piecewise linear integration
487 :
488 : // Get total width of polygon
489 16 : Real64 minX(v2d[0].x), maxX(v2d[0].x);
490 80 : for (Vertices::size_type i = 0; i < n; ++i) {
491 64 : Vertex2D const &v(v2d[i]);
492 64 : minX = std::min(minX, v.x);
493 64 : maxX = std::max(maxX, v.x);
494 : }
495 16 : Real64 totalWidth = maxX - minX;
496 :
497 16 : if (totalWidth == 0.0) {
498 : // This should never happen, but if it does, print a somewhat meaningful fatal error
499 : // (instead of allowing a divide by zero).
500 0 : ShowFatalError(state, format("Calculated projected surface width is zero for surface=\"{}\"", Name));
501 : }
502 :
503 16 : Real64 averageHeight = 0.0;
504 80 : for (Vertices::size_type i = 0; i < n; ++i) {
505 64 : Vertex2D const &v(v2d[i]);
506 :
507 : Vertex2D *v2;
508 64 : if (i == n - 1) {
509 16 : v2 = &v2d[0];
510 : } else {
511 48 : v2 = &v2d[i + 1];
512 : }
513 64 : averageHeight += 0.5 * (v.y + v2->y) * (v2->x - v.x) / totalWidth;
514 : }
515 16 : return std::abs(averageHeight) / SinTilt;
516 16 : }
517 :
518 579 : void SurfaceData::make_hash_key(EnergyPlusData &state, const int SurfNum)
519 : {
520 579 : auto &s_surf = state.dataSurface;
521 :
522 579 : calcHashKey = SurfaceCalcHashKey();
523 579 : calcHashKey.Construction = Construction;
524 579 : calcHashKey.Azimuth = round(Azimuth * 10.0) / 10.0;
525 579 : calcHashKey.Tilt = round(Tilt * 10.0) / 10.0;
526 579 : calcHashKey.Height = round(Height * 10.0) / 10.0;
527 579 : calcHashKey.Zone = Zone;
528 579 : calcHashKey.EnclIndex = SolarEnclIndex;
529 579 : calcHashKey.TAirRef = s_surf->SurfTAirRef(SurfNum);
530 :
531 579 : int extBoundCond = s_surf->Surface(SurfNum).ExtBoundCond;
532 579 : if (extBoundCond > 0) {
533 256 : calcHashKey.ExtZone = s_surf->Surface(extBoundCond).Zone;
534 256 : calcHashKey.ExtEnclIndex = s_surf->Surface(extBoundCond).SolarEnclIndex;
535 256 : calcHashKey.ExtCond = 1;
536 : } else {
537 323 : calcHashKey.ExtZone = 0;
538 323 : calcHashKey.ExtEnclIndex = 0;
539 323 : calcHashKey.ExtCond = extBoundCond;
540 : }
541 :
542 579 : calcHashKey.ExtSolar = ExtSolar;
543 579 : calcHashKey.ExtWind = ExtWind;
544 579 : calcHashKey.ViewFactorGround = round(ViewFactorGround * 10.0) / 10.0;
545 579 : calcHashKey.ViewFactorSky = round(ViewFactorSky * 10.0) / 10.0;
546 :
547 579 : calcHashKey.HeatTransferAlgorithm = HeatTransferAlgorithm;
548 579 : calcHashKey.intConvModel = s_surf->surfIntConv(SurfNum).model;
549 579 : calcHashKey.extConvModel = s_surf->surfExtConv(SurfNum).model;
550 579 : calcHashKey.intConvUserModelNum = s_surf->surfIntConv(SurfNum).userModelNum;
551 579 : calcHashKey.extConvUserModelNum = s_surf->surfExtConv(SurfNum).userModelNum;
552 579 : calcHashKey.OSCPtr = OSCPtr;
553 579 : calcHashKey.OSCMPtr = OSCMPtr;
554 :
555 579 : calcHashKey.FrameDivider = FrameDivider;
556 579 : calcHashKey.SurfWinStormWinConstr = s_surf->SurfWinStormWinConstr(SurfNum);
557 :
558 579 : calcHashKey.MaterialMovInsulExt = s_surf->extMovInsuls(SurfNum).matNum;
559 579 : calcHashKey.MaterialMovInsulInt = s_surf->intMovInsuls(SurfNum).matNum;
560 579 : calcHashKey.movInsulExtSchedNum = (s_surf->extMovInsuls(SurfNum).sched == nullptr) ? -1 : s_surf->extMovInsuls(SurfNum).sched->Num;
561 579 : calcHashKey.movInsulIntSchedNum = (s_surf->intMovInsuls(SurfNum).sched == nullptr) ? -1 : s_surf->intMovInsuls(SurfNum).sched->Num;
562 :
563 579 : calcHashKey.externalShadingSchedNum =
564 579 : (s_surf->Surface(SurfNum).surfExternalShadingSched != nullptr) ? s_surf->Surface(SurfNum).surfExternalShadingSched->Num : -1;
565 579 : calcHashKey.SurroundingSurfacesNum = s_surf->Surface(SurfNum).SurfSurroundingSurfacesNum;
566 579 : calcHashKey.LinkedOutAirNode = s_surf->Surface(SurfNum).SurfLinkedOutAirNode;
567 579 : calcHashKey.outsideHeatSourceTermSchedNum = (outsideHeatSourceTermSched != nullptr) ? outsideHeatSourceTermSched->Num : -1;
568 579 : calcHashKey.insideHeatSourceTermSchedNum = (insideHeatSourceTermSched != nullptr) ? insideHeatSourceTermSched->Num : -1;
569 579 : calcHashKey.ViewFactorSrdSurfs = s_surf->Surface(SurfNum).ViewFactorSrdSurfs;
570 579 : }
571 :
572 579 : void SurfaceData::set_representative_surface(EnergyPlusData &state, const int SurfNum)
573 : {
574 : // Make hash key for this surface (used to determine uniqueness)
575 579 : state.dataSurface->Surface(SurfNum).make_hash_key(state, SurfNum);
576 : // Insert surface key into map. If key already exists, it will not be added.
577 : // Assign the representative surface number based on the first instance of the identical key
578 579 : state.dataSurface->Surface(SurfNum).RepresentativeCalcSurfNum =
579 1158 : state.dataSurface->RepresentativeSurfaceMap.insert({state.dataSurface->Surface(SurfNum).calcHashKey, SurfNum}).first->second;
580 :
581 579 : state.dataSurface->Surface(state.dataSurface->Surface(SurfNum).RepresentativeCalcSurfNum).ConstituentSurfaceNums.push_back(SurfNum);
582 579 : }
583 :
584 : // Functions
585 :
586 2828408 : void SetSurfaceOutBulbTempAt(EnergyPlusData &state)
587 : {
588 2828408 : if (state.dataEnvrn->SiteTempGradient == 0.0) {
589 0 : for (int SurfNum = 1; SurfNum <= state.dataSurface->TotSurfaces; SurfNum++) {
590 0 : state.dataSurface->SurfOutDryBulbTemp(SurfNum) = state.dataEnvrn->OutDryBulbTemp;
591 0 : state.dataSurface->SurfOutWetBulbTemp(SurfNum) = state.dataEnvrn->OutWetBulbTemp;
592 : }
593 : } else {
594 2828408 : Real64 const BaseDryTemp(state.dataEnvrn->OutDryBulbTemp + state.dataEnvrn->WeatherFileTempModCoeff);
595 2828408 : Real64 const BaseWetTemp(state.dataEnvrn->OutWetBulbTemp + state.dataEnvrn->WeatherFileTempModCoeff);
596 185371798 : for (int SurfNum = 1; SurfNum <= state.dataSurface->TotSurfaces; SurfNum++) {
597 : // Base temperatures at Z = 0 (C)
598 182543390 : Real64 const Z(state.dataSurface->Surface(SurfNum).Centroid.z); // Centroid value
599 182543390 : if (Z <= 0.0) {
600 25080723 : state.dataSurface->SurfOutDryBulbTemp(SurfNum) = BaseDryTemp;
601 25080723 : state.dataSurface->SurfOutWetBulbTemp(SurfNum) = BaseWetTemp;
602 : } else {
603 157462667 : Real64 GradientDividend = state.dataEnvrn->SiteTempGradient * DataEnvironment::EarthRadius * Z;
604 157462667 : Real64 GradientDivisor = DataEnvironment::EarthRadius + Z;
605 157462667 : state.dataSurface->SurfOutDryBulbTemp(SurfNum) = BaseDryTemp - GradientDividend / GradientDivisor;
606 157462667 : state.dataSurface->SurfOutWetBulbTemp(SurfNum) = BaseWetTemp - GradientDividend / GradientDivisor;
607 : }
608 : }
609 : }
610 2828408 : }
611 :
612 2828408 : void CheckSurfaceOutBulbTempAt(EnergyPlusData &state)
613 : {
614 : // Using/Aliasing
615 : using DataEnvironment::SetOutBulbTempAt_error;
616 :
617 2828408 : Real64 minBulb = 0.0;
618 185371798 : for (int SurfNum = 1; SurfNum <= state.dataSurface->TotSurfaces; SurfNum++) {
619 182543390 : minBulb = min(minBulb, state.dataSurface->SurfOutDryBulbTemp(SurfNum), state.dataSurface->SurfOutWetBulbTemp(SurfNum));
620 182543390 : if (minBulb < -100.0) {
621 0 : SetOutBulbTempAt_error(state, "Surface", state.dataSurface->Surface(SurfNum).Centroid.z, state.dataSurface->Surface(SurfNum).Name);
622 : }
623 : }
624 2828408 : }
625 :
626 2828408 : void SetSurfaceWindSpeedAt(EnergyPlusData &state)
627 : {
628 2828408 : Real64 const fac(state.dataEnvrn->WindSpeed * state.dataEnvrn->WeatherFileWindModCoeff *
629 2828408 : std::pow(state.dataEnvrn->SiteWindBLHeight, -state.dataEnvrn->SiteWindExp));
630 2828408 : if (state.dataEnvrn->SiteWindExp == 0.0) {
631 0 : for (int SurfNum = 1; SurfNum <= state.dataSurface->TotSurfaces; SurfNum++) {
632 0 : state.dataSurface->SurfOutWindSpeed(SurfNum) = state.dataEnvrn->WindSpeed;
633 : }
634 : } else {
635 :
636 185371798 : for (int SurfNum = 1; SurfNum <= state.dataSurface->TotSurfaces; SurfNum++) {
637 182543390 : if (!state.dataSurface->Surface(SurfNum).ExtWind) {
638 110926129 : continue;
639 : }
640 71617261 : Real64 const Z(state.dataSurface->Surface(SurfNum).Centroid.z); // Centroid value
641 71617261 : if (Z <= 0.0) {
642 72555 : state.dataSurface->SurfOutWindSpeed(SurfNum) = 0.0;
643 : } else {
644 : // [Met] - at meterological Station, Height of measurement is usually 10m above ground
645 : // LocalWindSpeed = Windspeed [Met] * (Wind Boundary LayerThickness [Met]/Height [Met])**Wind Exponent[Met] &
646 : // * (Height above ground / Site Wind Boundary Layer Thickness) ** Site Wind Exponent
647 71544706 : state.dataSurface->SurfOutWindSpeed(SurfNum) = fac * std::pow(Z, state.dataEnvrn->SiteWindExp);
648 : }
649 : }
650 : }
651 2828408 : }
652 :
653 2828408 : void SetSurfaceWindDirAt(EnergyPlusData &state)
654 : {
655 185371798 : for (int SurfNum = 1; SurfNum <= state.dataSurface->TotSurfaces; SurfNum++) {
656 182543390 : state.dataSurface->SurfOutWindDir(SurfNum) = state.dataEnvrn->WindDir;
657 : }
658 2828408 : }
659 :
660 41659 : std::string cSurfaceClass(SurfaceClass const ClassNo)
661 : {
662 :
663 : // FUNCTION INFORMATION:
664 : // AUTHOR Linda Lawrie
665 : // DATE WRITTEN May 2006
666 : // MODIFIED na
667 : // RE-ENGINEERED na
668 :
669 : // PURPOSE OF THIS FUNCTION:
670 : // This function returns a string based on class number.
671 :
672 : // Return value
673 41659 : std::string ClassName;
674 :
675 41659 : switch (ClassNo) {
676 19804 : case SurfaceClass::Wall: {
677 19804 : ClassName = "Wall";
678 19804 : } break;
679 6146 : case SurfaceClass::Floor: {
680 6146 : ClassName = "Floor";
681 6146 : } break;
682 5263 : case SurfaceClass::Roof: {
683 5263 : ClassName = "Roof";
684 5263 : } break;
685 5848 : case SurfaceClass::Window: {
686 5848 : ClassName = "Window";
687 5848 : } break;
688 0 : case SurfaceClass::GlassDoor: {
689 0 : ClassName = "Glass Door";
690 0 : } break;
691 425 : case SurfaceClass::Door: {
692 425 : ClassName = "Door";
693 425 : } break;
694 4 : case SurfaceClass::TDD_Dome: {
695 4 : ClassName = "TubularDaylightDome";
696 4 : } break;
697 0 : case SurfaceClass::TDD_Diffuser: {
698 0 : ClassName = "TubularDaylightDiffuser";
699 0 : } break;
700 3451 : case SurfaceClass::IntMass: {
701 3451 : ClassName = "Internal Mass";
702 3451 : } break;
703 630 : case SurfaceClass::Shading: {
704 630 : ClassName = "Shading";
705 630 : } break;
706 34 : case SurfaceClass::Detached_B: {
707 34 : ClassName = "Detached Shading:Building";
708 34 : } break;
709 54 : case SurfaceClass::Detached_F: {
710 54 : ClassName = "Detached Shading:Fixed";
711 54 : } break;
712 0 : default: {
713 0 : ClassName = "Invalid/Unknown";
714 0 : } break;
715 : }
716 :
717 41659 : return ClassName;
718 0 : }
719 186135 : Real64 AbsFrontSide(EnergyPlusData &state, int SurfNum)
720 : {
721 : Real64 AbsorptanceFromExteriorFrontSide =
722 186135 : (state.dataSurface->SurfWinExtBeamAbsByShade(SurfNum) + state.dataSurface->SurfWinExtDiffAbsByShade(SurfNum)) *
723 186135 : state.dataSurface->SurfWinShadeAbsFacFace1(SurfNum);
724 : Real64 AbsorptanceFromInteriorFrontSide =
725 186135 : (state.dataSurface->SurfWinIntBeamAbsByShade(SurfNum) + state.dataSurface->SurfWinIntSWAbsByShade(SurfNum)) *
726 186135 : state.dataSurface->SurfWinShadeAbsFacFace2(SurfNum);
727 186135 : return AbsorptanceFromExteriorFrontSide + AbsorptanceFromInteriorFrontSide;
728 : }
729 :
730 186135 : Real64 AbsBackSide(EnergyPlusData &state, int SurfNum)
731 : {
732 : Real64 AbsorptanceFromInteriorBackSide =
733 186135 : (state.dataSurface->SurfWinIntBeamAbsByShade(SurfNum) + state.dataSurface->SurfWinIntSWAbsByShade(SurfNum)) *
734 186135 : state.dataSurface->SurfWinShadeAbsFacFace1(SurfNum);
735 : Real64 AbsorptanceFromExteriorBackSide =
736 186135 : (state.dataSurface->SurfWinExtBeamAbsByShade(SurfNum) + state.dataSurface->SurfWinExtDiffAbsByShade(SurfNum)) *
737 186135 : state.dataSurface->SurfWinShadeAbsFacFace2(SurfNum);
738 186135 : return AbsorptanceFromExteriorBackSide + AbsorptanceFromInteriorBackSide;
739 : }
740 :
741 801 : void GetVariableAbsorptanceSurfaceList(EnergyPlusData &state)
742 : {
743 801 : if (!state.dataMaterial->AnyVariableAbsorptance) {
744 800 : return;
745 : }
746 7 : for (int surfNum : state.dataSurface->AllHTSurfaceList) {
747 6 : auto const &thisSurface = state.dataSurface->Surface(surfNum);
748 6 : auto const &thisConstruct = state.dataConstruction->Construct(thisSurface.Construction);
749 6 : if (thisConstruct.TotLayers == 0) {
750 0 : continue;
751 : }
752 6 : if (thisConstruct.LayerPoint(1) == 0) {
753 0 : continue; // error finding material number
754 : }
755 6 : auto const *mat = state.dataMaterial->materials(thisConstruct.LayerPoint(1));
756 6 : if (mat->group != Material::Group::Regular) {
757 0 : continue;
758 : }
759 :
760 6 : if (mat->absorpVarCtrlSignal != Material::VariableAbsCtrlSignal::Invalid) {
761 : // check for dynamic coating defined on interior surface
762 1 : if (thisSurface.ExtBoundCond != ExternalEnvironment) {
763 0 : ShowWarningError(state,
764 0 : format("MaterialProperty:VariableAbsorptance defined on an interior surface, {}. This VariableAbsorptance property "
765 : "will be ignored here",
766 0 : thisSurface.Name));
767 : } else {
768 1 : state.dataSurface->AllVaryAbsOpaqSurfaceList.push_back(surfNum);
769 : }
770 : }
771 1 : }
772 : // check for dynamic coating defined on the non-outside layer of a construction
773 5 : for (int ConstrNum = 1; ConstrNum <= state.dataHeatBal->TotConstructs; ++ConstrNum) {
774 4 : auto const &thisConstruct = state.dataConstruction->Construct(ConstrNum);
775 4 : for (int Layer = 2; Layer <= thisConstruct.TotLayers; ++Layer) {
776 0 : auto const *mat = state.dataMaterial->materials(thisConstruct.LayerPoint(Layer));
777 0 : if (mat->group != Material::Group::Regular) {
778 0 : continue;
779 : }
780 0 : if (mat->absorpVarCtrlSignal != Material::VariableAbsCtrlSignal::Invalid) {
781 0 : ShowWarningError(state,
782 0 : format("MaterialProperty:VariableAbsorptance defined on a inside-layer materials, {}. This VariableAbsorptance "
783 : "property will be ignored here",
784 0 : mat->Name));
785 : }
786 : }
787 : }
788 : } // GetVariableAbsorptanceSurfaceList()
789 :
790 0 : Compass4 AzimuthToCompass4(Real64 azimuth)
791 : {
792 0 : assert(azimuth >= 0.0 && azimuth < 360.0);
793 0 : for (int c4 = 0; c4 < static_cast<int>(Compass4::Num); ++c4) {
794 0 : Real64 lo = Compass4AzimuthLo[c4];
795 0 : Real64 hi = Compass4AzimuthHi[c4];
796 0 : if (lo > hi) {
797 0 : if (azimuth >= lo || azimuth < hi) {
798 0 : return static_cast<Compass4>(c4);
799 : }
800 : } else {
801 0 : if (azimuth >= lo && azimuth < hi) {
802 0 : return static_cast<Compass4>(c4);
803 : }
804 : }
805 : }
806 0 : assert(false);
807 : return Compass4::Invalid;
808 : }
809 :
810 30818 : Compass8 AzimuthToCompass8(Real64 azimuth)
811 : {
812 30818 : assert(azimuth >= 0.0 && azimuth < 360.0);
813 128296 : for (int c8 = 0; c8 < static_cast<int>(Compass8::Num); ++c8) {
814 128296 : Real64 lo = Compass8AzimuthLo[c8];
815 128296 : Real64 hi = Compass8AzimuthHi[c8];
816 128296 : if (lo > hi) {
817 30818 : if (azimuth >= lo || azimuth < hi) {
818 6296 : return static_cast<Compass8>(c8);
819 : }
820 : } else {
821 97478 : if (azimuth >= lo && azimuth < hi) {
822 24522 : return static_cast<Compass8>(c8);
823 : }
824 : }
825 : }
826 0 : assert(false);
827 : return Compass8::Invalid;
828 : }
829 :
830 : } // namespace EnergyPlus::DataSurfaces
|