LCOV - code coverage report
Current view: top level - EnergyPlus - HeatBalanceKivaManager.cc (source / functions) Hit Total Coverage
Test: lcov.output.filtered Lines: 486 646 75.2 %
Date: 2023-01-17 19:17:23 Functions: 21 27 77.8 %

          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             : 
      50             : // Kiva Headers
      51             : #include <libkiva/Errors.hpp>
      52             : #ifdef GROUND_PLOT
      53             : #include <EnergyPlus/DataStringGlobals.hh>
      54             : #include <libgroundplot/GroundPlot.hpp>
      55             : #endif
      56             : 
      57             : // EnergyPlus Headers
      58             : #include <EnergyPlus/Construction.hh>
      59             : #include <EnergyPlus/Data/EnergyPlusData.hh>
      60             : #include <EnergyPlus/DataEnvironment.hh>
      61             : #include <EnergyPlus/DataHVACGlobals.hh>
      62             : #include <EnergyPlus/DataHeatBalSurface.hh>
      63             : #include <EnergyPlus/DataHeatBalance.hh>
      64             : #include <EnergyPlus/DataSurfaces.hh>
      65             : #include <EnergyPlus/DataSystemVariables.hh>
      66             : #include <EnergyPlus/DataZoneControls.hh>
      67             : #include <EnergyPlus/HeatBalanceKivaManager.hh>
      68             : #include <EnergyPlus/InternalHeatGains.hh>
      69             : #include <EnergyPlus/Material.hh>
      70             : #include <EnergyPlus/ScheduleManager.hh>
      71             : #include <EnergyPlus/SurfaceGeometry.hh>
      72             : #include <EnergyPlus/ThermalComfort.hh>
      73             : #include <EnergyPlus/UtilityRoutines.hh>
      74             : #include <EnergyPlus/Vectors.hh>
      75             : #include <EnergyPlus/WeatherManager.hh>
      76             : #include <EnergyPlus/ZoneTempPredictorCorrector.hh>
      77             : 
      78             : namespace EnergyPlus::HeatBalanceKivaManager {
      79             : 
      80           0 : void kivaErrorCallback(const int messageType, const std::string message, void *contextPtr)
      81             : {
      82           0 :     if (!contextPtr) {
      83           0 :         throw FatalError(format("Unhandled Kiva Error: {}", message));
      84             :     }
      85           0 :     std::string fullMessage;
      86           0 :     std::pair<EnergyPlusData *, std::string> contextPair = *(std::pair<EnergyPlusData *, std::string> *)contextPtr;
      87           0 :     if (contextPair.second.size() > 0) {
      88           0 :         fullMessage = format("{}: {}", contextPair.second, message);
      89             :     } else {
      90           0 :         fullMessage = format("Kiva: {}", message);
      91             :     }
      92           0 :     if (messageType == Kiva::MSG_INFO) {
      93           0 :         ShowMessage(*contextPair.first, fullMessage);
      94           0 :     } else if (messageType == Kiva::MSG_WARN) {
      95           0 :         ShowWarningError(*contextPair.first, fullMessage);
      96             :     } else { // if (messageType == Kiva::MSG_ERR)
      97           0 :         ShowSevereError(*contextPair.first, fullMessage);
      98           0 :         ShowFatalError(*contextPair.first, "Kiva: Errors discovered, program terminates.");
      99             :     }
     100           0 : }
     101             : 
     102          27 : KivaInstanceMap::KivaInstanceMap(EnergyPlusData &state,
     103             :                                  Kiva::Foundation &foundation,
     104             :                                  int floorSurface,
     105             :                                  std::vector<int> wallSurfaces,
     106             :                                  int zoneNum,
     107             :                                  Real64 zoneAssumedTemperature,
     108             :                                  Real64 floorWeight,
     109             :                                  int constructionNum,
     110          27 :                                  KivaManager *kmPtr)
     111             :     : instance(foundation), floorSurface(floorSurface), wallSurfaces(wallSurfaces), zoneNum(zoneNum), zoneControlType(KIVAZONE_UNCONTROLLED),
     112          27 :       zoneControlNum(0), zoneAssumedTemperature(zoneAssumedTemperature), floorWeight(floorWeight), constructionNum(constructionNum), kmPtr(kmPtr)
     113             : {
     114             : 
     115          83 :     for (int i = 1; i <= state.dataZoneCtrls->NumTempControlledZones; ++i) {
     116          76 :         if (state.dataZoneCtrls->TempControlledZone(i).ActualZoneNum == zoneNum) {
     117          20 :             zoneControlType = KIVAZONE_TEMPCONTROL;
     118          20 :             zoneControlNum = i;
     119          20 :             break;
     120             :         }
     121             :     }
     122          27 :     for (int i = 1; i <= state.dataZoneCtrls->NumComfortControlledZones; ++i) {
     123           0 :         if (state.dataZoneCtrls->ComfortControlledZone(i).ActualZoneNum == zoneNum) {
     124           0 :             zoneControlType = KIVAZONE_COMFORTCONTROL;
     125           0 :             zoneControlNum = i;
     126           0 :             break;
     127             :         }
     128             :     }
     129          27 :     for (size_t i = 1; i <= state.dataZoneCtrls->StageControlledZone.size(); ++i) {
     130           0 :         if (state.dataZoneCtrls->StageControlledZone(i).ActualZoneNum == zoneNum) {
     131           0 :             zoneControlType = KIVAZONE_STAGEDCONTROL;
     132           0 :             zoneControlNum = i;
     133           0 :             break;
     134             :         }
     135             :     }
     136          27 : }
     137             : 
     138         201 : void KivaInstanceMap::initGround(EnergyPlusData &state, const KivaWeatherData &kivaWeather)
     139             : {
     140             : 
     141             : #ifdef GROUND_PLOT
     142             :     std::string constructionName;
     143             :     if (constructionNum == 0) {
     144             :         constructionName = "Default Footing Wall Construction";
     145             :     } else {
     146             :         constructionName = state.dataConstruction->Construct(constructionNum).Name;
     147             :     }
     148             : 
     149             :     ss.dir = format("{}/{} {:.2R} {}",
     150             :                     FileSystem::getAbsolutePath(state.dataStrGlobals->outDirPath),
     151             :                     state.dataSurface->Surface(floorSurface).Name,
     152             :                     instance.ground->foundation.foundationDepth,
     153             :                     constructionName);
     154             : 
     155             :     debugDir = ss.dir;
     156             :     plotNum = 0;
     157             :     double &l = instance.ground->foundation.reductionLength2;
     158             :     constexpr double width = 0.0;
     159             :     const double depth = instance.ground->foundation.foundationDepth + width / 2.0;
     160             :     const double range = max(width, depth);
     161             :     ss.xRange = {l - range / 2.0, l + range / 2.0};
     162             :     ss.yRange = {0.5, 0.5};
     163             :     ss.zRange = {-range, instance.ground->foundation.wall.heightAboveGrade};
     164             :     ss.grid = true;
     165             :     ss.contours = false;
     166             :     ss.size = 1600;
     167             :     // ss.plotType = Kiva::SnapshotSettings::P_FLUX;
     168             : 
     169             :     if (ss.plotType == Kiva::SnapshotSettings::P_FLUX) {
     170             :         ss.minValue = 0.0;
     171             :         ss.maxValue = 100.0;
     172             :     }
     173             : 
     174             :     gp = Kiva::GroundPlot(ss, instance.ground->domain, instance.ground->foundation);
     175             : #endif
     176             : 
     177         201 :     int numAccelaratedTimesteps = 3;
     178         201 :     int acceleratedTimestep = 30; // days
     179         201 :     int accDate = getAccDate(state, numAccelaratedTimesteps, acceleratedTimestep);
     180             :     // Initialize with steady state before accelerated timestepping
     181         201 :     instance.ground->foundation.numericalScheme = Kiva::Foundation::NS_STEADY_STATE;
     182         201 :     setInitialBoundaryConditions(state, kivaWeather, accDate, 24, state.dataGlobal->NumOfTimeStepInHour);
     183         201 :     instance.calculate();
     184         201 :     accDate += acceleratedTimestep;
     185         201 :     while (accDate > 365 + state.dataWeatherManager->LeapYearAdd) {
     186           0 :         accDate = accDate - (365 + state.dataWeatherManager->LeapYearAdd);
     187             :     }
     188             : 
     189             :     // Accelerated timestepping
     190         201 :     instance.ground->foundation.numericalScheme = Kiva::Foundation::NS_IMPLICIT;
     191         804 :     for (int i = 0; i < numAccelaratedTimesteps; ++i) {
     192         603 :         setInitialBoundaryConditions(state, kivaWeather, accDate, 24, state.dataGlobal->NumOfTimeStepInHour);
     193         603 :         instance.calculate(acceleratedTimestep * 24 * 60 * 60);
     194         603 :         accDate += acceleratedTimestep;
     195         753 :         while (accDate > 365 + state.dataWeatherManager->LeapYearAdd) {
     196          75 :             accDate = accDate - (365 + state.dataWeatherManager->LeapYearAdd);
     197             :         }
     198             :     }
     199             : 
     200         201 :     instance.calculate_surface_averages();
     201         201 :     instance.foundation->numericalScheme = Kiva::Foundation::NS_ADI;
     202         201 : }
     203             : 
     204         201 : int KivaInstanceMap::getAccDate(EnergyPlusData &state, const int numAccelaratedTimesteps, const int acceleratedTimestep)
     205             : {
     206             :     // Determine accelerated intervals
     207             :     int accDate =
     208         201 :         state.dataEnvrn->DayOfYear - 1 - acceleratedTimestep * (numAccelaratedTimesteps + 1); // date time = last timestep from the day before
     209         385 :     while (accDate <= 0) {
     210          92 :         accDate = accDate + 365 + state.dataWeatherManager->LeapYearAdd;
     211             :     }
     212         201 :     return accDate;
     213             : }
     214             : 
     215         804 : void KivaInstanceMap::setInitialBoundaryConditions(
     216             :     EnergyPlusData &state, const KivaWeatherData &kivaWeather, const int date, const int hour, const int timestep)
     217             : {
     218             : 
     219             :     unsigned index, indexPrev;
     220         804 :     unsigned dataSize = kivaWeather.windSpeed.size();
     221             :     Real64 weightNow;
     222             : 
     223         804 :     if (kivaWeather.intervalsPerHour == 1) {
     224         804 :         index = (date - 1) * 24 + (hour - 1);
     225         804 :         weightNow = min(1.0, (double(timestep) / double(state.dataGlobal->NumOfTimeStepInHour)));
     226             :     } else {
     227           0 :         index = (date - 1) * 24 * state.dataGlobal->NumOfTimeStepInHour + (hour - 1) * state.dataGlobal->NumOfTimeStepInHour + (timestep - 1);
     228           0 :         weightNow = 1.0; // weather data interval must be the same as the timestep interval (i.e., no interpolation)
     229             :     }
     230         804 :     if (index == 0) {
     231           0 :         indexPrev = dataSize - 1;
     232             :     } else {
     233         804 :         indexPrev = index - 1;
     234             :     }
     235             : 
     236         804 :     instance.bcs = std::make_shared<Kiva::BoundaryConditions>();
     237             : 
     238        1608 :     std::shared_ptr<Kiva::BoundaryConditions> bcs = instance.bcs;
     239             : 
     240         804 :     bcs->outdoorTemp = kivaWeather.dryBulb[index] * weightNow + kivaWeather.dryBulb[indexPrev] * (1.0 - weightNow) + DataGlobalConstants::KelvinConv;
     241             : 
     242        2412 :     bcs->localWindSpeed = (kivaWeather.windSpeed[index] * weightNow + kivaWeather.windSpeed[indexPrev] * (1.0 - weightNow)) *
     243        1608 :                           state.dataEnvrn->WeatherFileWindModCoeff *
     244         804 :                           std::pow(instance.ground->foundation.grade.roughness / state.dataEnvrn->SiteWindBLHeight, state.dataEnvrn->SiteWindExp);
     245         804 :     bcs->skyEmissivity = kivaWeather.skyEmissivity[index] * weightNow + kivaWeather.skyEmissivity[indexPrev] * (1.0 - weightNow);
     246         804 :     bcs->solarAzimuth = 3.14;
     247         804 :     bcs->solarAltitude = 0.0;
     248         804 :     bcs->directNormalFlux = 0.0;
     249         804 :     bcs->diffuseHorizontalFlux = 0.0;
     250         804 :     bcs->slabAbsRadiation = 0.0;
     251         804 :     bcs->wallAbsRadiation = 0.0;
     252         804 :     bcs->deepGroundTemperature = kivaWeather.annualAverageDrybulbTemp + DataGlobalConstants::KelvinConv;
     253             : 
     254             :     // Estimate indoor temperature
     255         804 :     constexpr Real64 defaultFlagTemp = -999;   // default sets this below -999 at -9999 so uses value if entered
     256         804 :     constexpr Real64 standardTemp = 22;        // degC
     257         804 :     Real64 assumedFloatingTemp = standardTemp; // degC (somewhat arbitrary assumption--not knowing anything else
     258             :                                                // about the building at this point)
     259             : 
     260             :     Real64 Tin;
     261         804 :     if (zoneAssumedTemperature > defaultFlagTemp) {
     262         180 :         Tin = zoneAssumedTemperature + DataGlobalConstants::KelvinConv;
     263             :     } else {
     264         624 :         switch (zoneControlType) {
     265         140 :         case KIVAZONE_UNCONTROLLED: {
     266         140 :             Tin = assumedFloatingTemp + DataGlobalConstants::KelvinConv;
     267         140 :             break;
     268             :         }
     269         484 :         case KIVAZONE_TEMPCONTROL: {
     270             : 
     271         484 :             int controlTypeSchId = state.dataZoneCtrls->TempControlledZone(zoneControlNum).CTSchedIndex;
     272             :             auto controlType =
     273         484 :                 static_cast<DataHVACGlobals::ThermostatType>(ScheduleManager::LookUpScheduleValue(state, controlTypeSchId, hour, timestep));
     274             : 
     275         484 :             switch (controlType) {
     276           0 :             case DataHVACGlobals::ThermostatType::Uncontrolled:
     277           0 :                 Tin = assumedFloatingTemp + DataGlobalConstants::KelvinConv;
     278           0 :                 break;
     279          36 :             case DataHVACGlobals::ThermostatType::SingleHeating: {
     280          36 :                 int schNameId = state.dataZoneCtrls->TempControlledZone(zoneControlNum).SchIndx_SingleHeatSetPoint;
     281          36 :                 Real64 setpoint = ScheduleManager::LookUpScheduleValue(state, schNameId, hour, timestep);
     282          36 :                 Tin = setpoint + DataGlobalConstants::KelvinConv;
     283          36 :                 break;
     284             :             }
     285          24 :             case DataHVACGlobals::ThermostatType::SingleCooling: {
     286          24 :                 int schNameId = state.dataZoneCtrls->TempControlledZone(zoneControlNum).SchIndx_SingleCoolSetPoint;
     287          24 :                 Real64 setpoint = ScheduleManager::LookUpScheduleValue(state, schNameId, hour, timestep);
     288          24 :                 Tin = setpoint + DataGlobalConstants::KelvinConv;
     289          24 :                 break;
     290             :             }
     291           0 :             case DataHVACGlobals::ThermostatType::SingleHeatCool: {
     292           0 :                 int schNameId = state.dataZoneCtrls->TempControlledZone(zoneControlNum).SchIndx_SingleHeatCoolSetPoint;
     293           0 :                 Real64 setpoint = ScheduleManager::LookUpScheduleValue(state, schNameId, hour, timestep);
     294           0 :                 Tin = setpoint + DataGlobalConstants::KelvinConv;
     295           0 :                 break;
     296             :             }
     297         424 :             case DataHVACGlobals::ThermostatType::DualSetPointWithDeadBand: {
     298         424 :                 int schNameIdHeat = state.dataZoneCtrls->TempControlledZone(zoneControlNum).SchIndx_DualSetPointWDeadBandHeat;
     299         424 :                 int schNameIdCool = state.dataZoneCtrls->TempControlledZone(zoneControlNum).SchIndx_DualSetPointWDeadBandCool;
     300         424 :                 Real64 heatSetpoint = ScheduleManager::LookUpScheduleValue(state, schNameIdHeat, hour, timestep);
     301         424 :                 Real64 coolSetpoint = ScheduleManager::LookUpScheduleValue(state, schNameIdCool, hour, timestep);
     302         424 :                 constexpr Real64 heatBalanceTemp = 10.0; // (assumed) degC
     303         424 :                 constexpr Real64 coolBalanceTemp = 15.0; // (assumed) degC
     304             : 
     305         424 :                 if (bcs->outdoorTemp < heatBalanceTemp) {
     306           0 :                     Tin = heatSetpoint + DataGlobalConstants::KelvinConv;
     307         424 :                 } else if (bcs->outdoorTemp > coolBalanceTemp) {
     308         424 :                     Tin = coolSetpoint + DataGlobalConstants::KelvinConv;
     309             :                 } else {
     310           0 :                     Real64 weight = (coolBalanceTemp - bcs->outdoorTemp) / (coolBalanceTemp - heatBalanceTemp);
     311           0 :                     Tin = heatSetpoint * weight + coolSetpoint * (1.0 - weight) + DataGlobalConstants::KelvinConv;
     312             :                 }
     313         424 :                 break;
     314             :             }
     315           0 :             default:
     316           0 :                 Tin = 0.0;
     317           0 :                 ShowSevereError(state,
     318           0 :                                 format("Illegal control type for Zone={}, Found value={}, in Schedule={}",
     319           0 :                                        state.dataHeatBal->Zone(zoneNum).Name,
     320             :                                        controlType,
     321           0 :                                        state.dataZoneCtrls->TempControlledZone(zoneControlNum).ControlTypeSchedName));
     322             :             }
     323         484 :             break;
     324             :         }
     325           0 :         case KIVAZONE_COMFORTCONTROL: {
     326             : 
     327           0 :             Tin = standardTemp + DataGlobalConstants::KelvinConv;
     328           0 :             break;
     329             :         }
     330           0 :         case KIVAZONE_STAGEDCONTROL: {
     331             : 
     332           0 :             int heatSpSchId = state.dataZoneCtrls->StageControlledZone(zoneControlNum).HSBchedIndex;
     333           0 :             int coolSpSchId = state.dataZoneCtrls->StageControlledZone(zoneControlNum).CSBchedIndex;
     334           0 :             Real64 heatSetpoint = ScheduleManager::LookUpScheduleValue(state, heatSpSchId, hour, timestep);
     335           0 :             Real64 coolSetpoint = ScheduleManager::LookUpScheduleValue(state, coolSpSchId, hour, timestep);
     336           0 :             constexpr Real64 heatBalanceTemp = 10.0; // (assumed) degC
     337           0 :             constexpr Real64 coolBalanceTemp = 15.0; // (assumed) degC
     338           0 :             if (bcs->outdoorTemp < heatBalanceTemp) {
     339           0 :                 Tin = heatSetpoint + DataGlobalConstants::KelvinConv;
     340           0 :             } else if (bcs->outdoorTemp > coolBalanceTemp) {
     341           0 :                 Tin = coolSetpoint + DataGlobalConstants::KelvinConv;
     342             :             } else {
     343           0 :                 Real64 weight = (coolBalanceTemp - bcs->outdoorTemp) / (coolBalanceTemp - heatBalanceTemp);
     344           0 :                 Tin = heatSetpoint * weight + coolSetpoint * (1.0 - weight) + DataGlobalConstants::KelvinConv;
     345             :             }
     346           0 :             break;
     347             :         }
     348           0 :         default: {
     349             :             // error?
     350           0 :             Tin = assumedFloatingTemp + DataGlobalConstants::KelvinConv;
     351           0 :             break;
     352             :         }
     353             :         }
     354             :     }
     355         804 :     bcs->slabConvectiveTemp = bcs->wallConvectiveTemp = bcs->slabRadiantTemp = bcs->wallRadiantTemp = Tin;
     356             : 
     357         804 :     bcs->gradeForcedTerm = kmPtr->surfaceConvMap[floorSurface].f;
     358         804 :     bcs->gradeConvectionAlgorithm = kmPtr->surfaceConvMap[floorSurface].out;
     359         804 :     bcs->slabConvectionAlgorithm = kmPtr->surfaceConvMap[floorSurface].in;
     360             : 
     361         804 :     if (!wallSurfaces.empty()) {
     362         120 :         bcs->extWallForcedTerm = kmPtr->surfaceConvMap[wallSurfaces[0]].f;
     363         120 :         bcs->extWallConvectionAlgorithm = kmPtr->surfaceConvMap[wallSurfaces[0]].out;
     364         120 :         bcs->intWallConvectionAlgorithm = kmPtr->surfaceConvMap[wallSurfaces[0]].in;
     365             :     } else {
     366             :         // If no wall surfaces, assume that any exposed foundation wall in Kiva uses
     367             :         // same algorithm as exterior grade
     368         684 :         bcs->extWallForcedTerm = kmPtr->surfaceConvMap[floorSurface].f;
     369         684 :         bcs->extWallConvectionAlgorithm = kmPtr->surfaceConvMap[floorSurface].out;
     370             :         // No interior walls
     371             :     }
     372         804 : }
     373             : 
     374        2016 : void KivaInstanceMap::setBoundaryConditions(EnergyPlusData &state)
     375             : {
     376        4032 :     std::shared_ptr<Kiva::BoundaryConditions> bcs = instance.bcs;
     377             : 
     378        2016 :     bcs->outdoorTemp = state.dataEnvrn->OutDryBulbTemp + DataGlobalConstants::KelvinConv;
     379        2016 :     bcs->localWindSpeed = DataEnvironment::WindSpeedAt(state, instance.ground->foundation.grade.roughness);
     380        2016 :     bcs->windDirection = state.dataEnvrn->WindDir * DataGlobalConstants::DegToRadians;
     381        2016 :     bcs->solarAzimuth = std::atan2(state.dataEnvrn->SOLCOS(1), state.dataEnvrn->SOLCOS(2));
     382        2016 :     bcs->solarAltitude = DataGlobalConstants::PiOvr2 - std::acos(state.dataEnvrn->SOLCOS(3));
     383        2016 :     bcs->directNormalFlux = state.dataEnvrn->BeamSolarRad;
     384        2016 :     bcs->diffuseHorizontalFlux = state.dataEnvrn->DifSolarRad;
     385        2016 :     bcs->skyEmissivity = pow4(state.dataEnvrn->SkyTempKelvin) / pow4(bcs->outdoorTemp);
     386             : 
     387        6048 :     bcs->slabAbsRadiation = state.dataHeatBalSurf->SurfOpaqQRadSWInAbs(floorSurface) +      // solar
     388        4032 :                             state.dataHeatBal->SurfQdotRadIntGainsInPerArea(floorSurface) + // internal gains
     389        2016 :                             state.dataHeatBalSurf->SurfQdotRadHVACInPerArea(floorSurface);  // HVAC
     390             : 
     391        2016 :     bcs->slabConvectiveTemp = state.dataHeatBal->SurfTempEffBulkAir(floorSurface) + DataGlobalConstants::KelvinConv;
     392        2016 :     bcs->slabRadiantTemp = ThermalComfort::CalcSurfaceWeightedMRT(state, floorSurface, false) + DataGlobalConstants::KelvinConv;
     393        2016 :     bcs->gradeForcedTerm = kmPtr->surfaceConvMap[floorSurface].f;
     394        2016 :     bcs->gradeConvectionAlgorithm = kmPtr->surfaceConvMap[floorSurface].out;
     395        2016 :     bcs->slabConvectionAlgorithm = kmPtr->surfaceConvMap[floorSurface].in;
     396             : 
     397             :     // Calculate area weighted average for walls
     398        2016 :     Real64 QAtotal = 0.0;
     399        2016 :     Real64 Atotal = 0.0;
     400        2016 :     Real64 TARadTotal = 0.0;
     401        2016 :     Real64 TAConvTotal = 0.0;
     402        2736 :     for (auto &wl : wallSurfaces) {
     403        1440 :         Real64 Q = state.dataHeatBalSurf->SurfOpaqQRadSWInAbs(wl) +      // solar
     404         720 :                    state.dataHeatBal->SurfQdotRadIntGainsInPerArea(wl) + // internal gains
     405         720 :                    state.dataHeatBalSurf->SurfQdotRadHVACInPerArea(wl);  // HVAC
     406             : 
     407         720 :         Real64 &A = state.dataSurface->Surface(wl).Area;
     408             : 
     409         720 :         Real64 Trad = ThermalComfort::CalcSurfaceWeightedMRT(state, wl, false);
     410         720 :         Real64 Tconv = state.dataHeatBal->SurfTempEffBulkAir(wl);
     411             : 
     412         720 :         QAtotal += Q * A;
     413         720 :         TARadTotal += Trad * A;
     414         720 :         TAConvTotal += Tconv * A;
     415         720 :         Atotal += A;
     416             :     }
     417             : 
     418        2016 :     if (Atotal > 0.0) {
     419         288 :         bcs->wallAbsRadiation = QAtotal / Atotal;
     420         288 :         bcs->wallRadiantTemp = TARadTotal / Atotal + DataGlobalConstants::KelvinConv;
     421         288 :         bcs->wallConvectiveTemp = TAConvTotal / Atotal + DataGlobalConstants::KelvinConv;
     422         288 :         bcs->extWallForcedTerm = kmPtr->surfaceConvMap[wallSurfaces[0]].f;
     423         288 :         bcs->extWallConvectionAlgorithm = kmPtr->surfaceConvMap[wallSurfaces[0]].out;
     424         288 :         bcs->intWallConvectionAlgorithm = kmPtr->surfaceConvMap[wallSurfaces[0]].in;
     425             :     } else { // No wall surfaces
     426             :         // If no wall surfaces, assume that any exposed foundation wall in Kiva uses
     427             :         // same algorithm as exterior grade
     428        1728 :         bcs->extWallForcedTerm = kmPtr->surfaceConvMap[floorSurface].f;
     429        1728 :         bcs->extWallConvectionAlgorithm = kmPtr->surfaceConvMap[floorSurface].out;
     430             :     }
     431        2016 : }
     432             : 
     433         771 : KivaManager::Settings::Settings()
     434             :     : soilK(0.864), soilRho(1510), soilCp(1260), groundSolarAbs(0.9), groundThermalAbs(0.9), groundRoughness(0.9), farFieldWidth(40.0),
     435         771 :       deepGroundBoundary(AUTO), deepGroundDepth(40.0), autocalculateDeepGroundDepth(true), minCellDim(0.02), maxGrowthCoeff(1.5), timestepType(HOURLY)
     436             : {
     437         771 : }
     438             : 
     439           6 : KivaManager::WallGroup::WallGroup(Real64 exposedPerimeter, std::vector<int> wallIDs) : exposedPerimeter(exposedPerimeter), wallIDs(wallIDs)
     440             : {
     441           6 : }
     442             : 
     443           6 : KivaManager::WallGroup::WallGroup() : exposedPerimeter(0.0)
     444             : {
     445           6 : }
     446             : 
     447         771 : KivaManager::KivaManager() : timestep(3600), defaultAdded(false), defaultIndex(0)
     448             : {
     449         771 : }
     450             : 
     451         771 : KivaManager::~KivaManager()
     452             : {
     453         771 : }
     454             : 
     455           9 : void KivaManager::readWeatherData(EnergyPlusData &state)
     456             : {
     457             :     // Below from OpenEPlusWeatherFile
     458          18 :     auto kivaWeatherFile = state.files.inputWeatherFilePath.open(state, "KivaManager::readWeatherFile");
     459             : 
     460             :     // Read in Header Information
     461             :     static Array1D_string const Header(8,
     462             :                                        {"LOCATION",
     463             :                                         "DESIGN CONDITIONS",
     464             :                                         "TYPICAL/EXTREME PERIODS",
     465             :                                         "GROUND TEMPERATURES",
     466             :                                         "HOLIDAYS/DAYLIGHT SAVING",
     467             :                                         "COMMENTS 1",
     468             :                                         "COMMENTS 2",
     469           9 :                                         "DATA PERIODS"});
     470             : 
     471           9 :     int HdLine = 1; // Look for first Header
     472           9 :     bool StillLooking = true;
     473         153 :     while (StillLooking) {
     474         144 :         auto LineResult = kivaWeatherFile.readLine();
     475          72 :         if (LineResult.eof) {
     476           0 :             ShowFatalError(
     477             :                 state,
     478           0 :                 "Kiva::ReadWeatherFile: Unexpected End-of-File on EPW Weather file, while reading header information, looking for header=" +
     479             :                     Header(HdLine));
     480             :         }
     481             : 
     482             :         // Use headers to know how to read data to memory (e.g., number of periods, number of intervals)
     483          72 :         int endcol = LineResult.data.size();
     484          72 :         if (endcol > 0) {
     485          72 :             if (int(LineResult.data[endcol - 1]) == DataSystemVariables::iUnicode_end) {
     486           0 :                 ShowSevereError(state, "OpenWeatherFile: EPW Weather File appears to be a Unicode or binary file.");
     487           0 :                 ShowContinueError(state, "...This file cannot be read by this program. Please save as PC or Unix file and try again");
     488           0 :                 ShowFatalError(state, "Program terminates due to previous condition.");
     489             :             }
     490             :         }
     491          72 :         std::string::size_type Pos = FindNonSpace(LineResult.data);
     492          72 :         std::string::size_type const HdPos = index(LineResult.data, Header(HdLine));
     493          72 :         if (Pos != HdPos) continue;
     494          72 :         Pos = index(LineResult.data, ',');
     495             : 
     496             :         // Below borrowed from ProcessEPWHeader
     497             : 
     498          72 :         if ((Pos == std::string::npos) && (!has_prefixi(Header(HdLine), "COMMENTS"))) {
     499           0 :             ShowSevereError(state, "Invalid Header line in in.epw -- no commas");
     500           0 :             ShowContinueError(state, "Line=" + LineResult.data);
     501           0 :             ShowFatalError(state, "Previous conditions cause termination.");
     502             :         }
     503          72 :         if (Pos != std::string::npos) LineResult.data.erase(0, Pos + 1);
     504             : 
     505          72 :         if (UtilityRoutines::MakeUPPERCase(Header(HdLine)) == "DATA PERIODS") {
     506             :             bool IOStatus;
     507           9 :             uppercase(LineResult.data);
     508           9 :             int NumHdArgs = 2;
     509           9 :             int Count = 1;
     510         117 :             while (Count <= NumHdArgs) {
     511          54 :                 strip(LineResult.data);
     512          54 :                 Pos = index(LineResult.data, ',');
     513          54 :                 if (Pos == std::string::npos) {
     514           9 :                     if (len(LineResult.data) == 0) {
     515           0 :                         while (Pos == std::string::npos) {
     516           0 :                             LineResult.update(kivaWeatherFile.readLine());
     517           0 :                             strip(LineResult.data);
     518           0 :                             uppercase(LineResult.data);
     519           0 :                             Pos = index(LineResult.data, ',');
     520             :                         }
     521             :                     } else {
     522           9 :                         Pos = len(LineResult.data);
     523             :                     }
     524             :                 }
     525             : 
     526          54 :                 switch (Count) {
     527           9 :                 case 1:
     528           9 :                     NumHdArgs += 4 * UtilityRoutines::ProcessNumber(LineResult.data.substr(0, Pos), IOStatus);
     529             :                     // TODO: Error if more than one period? Less than full year?
     530           9 :                     break;
     531           9 :                 case 2:
     532           9 :                     kivaWeather.intervalsPerHour = UtilityRoutines::ProcessNumber(LineResult.data.substr(0, Pos), IOStatus);
     533           9 :                     break;
     534          36 :                 default:
     535          36 :                     break;
     536             :                 }
     537          54 :                 LineResult.data.erase(0, Pos + 1);
     538          54 :                 ++Count;
     539             :             }
     540             :         }
     541             : 
     542          72 :         ++HdLine;
     543          72 :         if (HdLine == 9) StillLooking = false;
     544             :     }
     545             : 
     546           9 :     bool ErrorFound = false;
     547             :     int WYear;
     548             :     int WMonth;
     549             :     int WDay;
     550             :     int WHour;
     551             :     int WMinute;
     552             :     Real64 DryBulb;
     553             :     Real64 DewPoint;
     554             :     Real64 RelHum;
     555             :     Real64 AtmPress;
     556             :     Real64 ETHoriz;
     557             :     Real64 ETDirect;
     558             :     Real64 IRHoriz;
     559             :     Real64 GLBHoriz;
     560             :     Real64 DirectRad;
     561             :     Real64 DiffuseRad;
     562             :     Real64 GLBHorizIllum;
     563             :     Real64 DirectNrmIllum;
     564             :     Real64 DiffuseHorizIllum;
     565             :     Real64 ZenLum;
     566             :     Real64 WindDir;
     567             :     Real64 WindSpeed;
     568             :     Real64 TotalSkyCover;
     569             :     Real64 OpaqueSkyCover;
     570             :     Real64 Visibility;
     571             :     Real64 CeilHeight;
     572             :     Real64 PrecipWater;
     573             :     Real64 AerosolOptDepth;
     574             :     Real64 SnowDepth;
     575             :     Real64 DaysSinceLastSnow;
     576             :     Real64 Albedo;
     577             :     Real64 LiquidPrecip;
     578             :     int PresWeathObs;
     579          18 :     Array1D_int PresWeathConds(9);
     580             : 
     581           9 :     Real64 totalDB = 0.0;
     582           9 :     int count = 0;
     583             : 
     584             :     while (true) {
     585      157689 :         auto WeatherDataLine = kivaWeatherFile.readLine();
     586       78849 :         if (WeatherDataLine.eof) {
     587           9 :             break;
     588             :         }
     589       78840 :         WeatherManager::InterpretWeatherDataLine(state,
     590             :                                                  WeatherDataLine.data,
     591             :                                                  ErrorFound,
     592             :                                                  WYear,
     593             :                                                  WMonth,
     594             :                                                  WDay,
     595             :                                                  WHour,
     596             :                                                  WMinute,
     597             :                                                  DryBulb,
     598             :                                                  DewPoint,
     599             :                                                  RelHum,
     600             :                                                  AtmPress,
     601             :                                                  ETHoriz,
     602             :                                                  ETDirect,
     603             :                                                  IRHoriz,
     604             :                                                  GLBHoriz,
     605             :                                                  DirectRad,
     606             :                                                  DiffuseRad,
     607             :                                                  GLBHorizIllum,
     608             :                                                  DirectNrmIllum,
     609             :                                                  DiffuseHorizIllum,
     610             :                                                  ZenLum,
     611             :                                                  WindDir,
     612             :                                                  WindSpeed,
     613             :                                                  TotalSkyCover,
     614             :                                                  OpaqueSkyCover,
     615             :                                                  Visibility,
     616             :                                                  CeilHeight,
     617             :                                                  PresWeathObs,
     618             :                                                  PresWeathConds,
     619             :                                                  PrecipWater,
     620             :                                                  AerosolOptDepth,
     621             :                                                  SnowDepth,
     622             :                                                  DaysSinceLastSnow,
     623             :                                                  Albedo,
     624             :                                                  LiquidPrecip);
     625             : 
     626             :         // Checks for missing value
     627       78840 :         if (DryBulb >= 99.9) {
     628           0 :             DryBulb = state.dataWeatherManager->Missing.DryBulb;
     629             :         }
     630       78840 :         if (DewPoint >= 99.9) {
     631           0 :             DewPoint = state.dataWeatherManager->Missing.DewPoint;
     632             :         }
     633       78840 :         if (WindSpeed >= 999.0) {
     634           0 :             WindSpeed = state.dataWeatherManager->Missing.WindSpd;
     635             :         }
     636       78840 :         if (OpaqueSkyCover >= 99.0) {
     637           0 :             OpaqueSkyCover = state.dataWeatherManager->Missing.OpaqSkyCvr;
     638             :         }
     639             : 
     640       78840 :         kivaWeather.dryBulb.push_back(DryBulb);
     641       78840 :         kivaWeather.windSpeed.push_back(WindSpeed);
     642             : 
     643       78840 :         Real64 OSky = OpaqueSkyCover;
     644       78840 :         Real64 TDewK = min(DryBulb, DewPoint) + DataGlobalConstants::KelvinConv;
     645      157680 :         Real64 ESky = (0.787 + 0.764 * std::log(TDewK / DataGlobalConstants::KelvinConv)) *
     646      157680 :                       (1.0 + 0.0224 * OSky - 0.0035 * pow_2(OSky) + 0.00028 * pow_3(OSky));
     647             : 
     648       78840 :         kivaWeather.skyEmissivity.push_back(ESky);
     649             : 
     650       78840 :         ++count;
     651       78840 :         totalDB += DryBulb;
     652       78840 :     }
     653             : 
     654             :     // Annual averages
     655           9 :     kivaWeather.annualAverageDrybulbTemp = totalDB / count;
     656           9 : }
     657             : 
     658           9 : bool KivaManager::setupKivaInstances(EnergyPlusData &state)
     659             : {
     660          18 :     std::pair<EnergyPlusData *, std::string> contextPair{&state, ""};
     661           9 :     Kiva::setMessageCallback(kivaErrorCallback, &contextPair);
     662           9 :     bool ErrorsFound = false;
     663             : 
     664           9 :     if (state.dataZoneCtrls->GetZoneAirStatsInputFlag) {
     665           9 :         ZoneTempPredictorCorrector::GetZoneAirSetPoints(state);
     666           9 :         state.dataZoneCtrls->GetZoneAirStatsInputFlag = false;
     667             :     }
     668             : 
     669           9 :     readWeatherData(state);
     670             : 
     671           9 :     auto &Surfaces = state.dataSurface->Surface;
     672           9 :     auto &Constructs = state.dataConstruction->Construct;
     673           9 :     auto &Materials = state.dataMaterial->Material;
     674             : 
     675           9 :     int inst = 0;
     676           9 :     int surfNum = 1;
     677             : 
     678         499 :     for (auto &surface : Surfaces) {
     679         490 :         if (surface.ExtBoundCond == DataSurfaces::KivaFoundation && surface.Class == DataSurfaces::SurfaceClass::Floor) {
     680             : 
     681             :             // Find other surfaces associated with the same floor
     682          46 :             std::vector<int> wallSurfaces;
     683             : 
     684         127 :             for (auto &wl : foundationInputs[surface.OSCPtr].surfaces) {
     685         104 :                 if (Surfaces(wl).Zone == surface.Zone && wl != surfNum) {
     686          15 :                     if (Surfaces(wl).Class != DataSurfaces::SurfaceClass::Wall) {
     687           0 :                         if (Surfaces(wl).Class == DataSurfaces::SurfaceClass::Floor) {
     688           0 :                             ErrorsFound = true;
     689           0 :                             ShowSevereError(state,
     690           0 :                                             "Foundation:Kiva=\"" + foundationInputs[surface.OSCPtr].name +
     691             :                                                 "\", only one floor per Foundation:Kiva Object allowed.");
     692             :                         } else {
     693           0 :                             ErrorsFound = true;
     694           0 :                             ShowSevereError(state,
     695           0 :                                             "Foundation:Kiva=\"" + foundationInputs[surface.OSCPtr].name +
     696             :                                                 "\", only floor and wall surfaces are allowed to reference Foundation Outside Boundary Conditions.");
     697           0 :                             ShowContinueError(state, "Surface=\"" + Surfaces(wl).Name + "\", is not a floor or wall.");
     698             :                         }
     699             :                     } else {
     700          15 :                         wallSurfaces.push_back(wl);
     701             :                     }
     702             :                 }
     703             :             }
     704             : 
     705             :             // Calculate total exposed perimeter attributes
     706          46 :             std::vector<bool> isExposedPerimeter;
     707             : 
     708          23 :             bool userSetExposedPerimeter = false;
     709          23 :             bool useDetailedExposedPerimeter = false;
     710          23 :             Real64 exposedFraction = 0.0;
     711             : 
     712          23 :             auto &expPerimMap = state.dataSurfaceGeometry->exposedFoundationPerimeter.surfaceMap;
     713          23 :             if (expPerimMap.count(surfNum) == 1) {
     714          23 :                 userSetExposedPerimeter = true;
     715          23 :                 useDetailedExposedPerimeter = expPerimMap[surfNum].useDetailedExposedPerimeter;
     716          23 :                 if (useDetailedExposedPerimeter) {
     717          95 :                     for (auto s : expPerimMap[surfNum].isExposedPerimeter) {
     718          76 :                         isExposedPerimeter.push_back(s);
     719             :                     }
     720             :                 } else {
     721           4 :                     exposedFraction = expPerimMap[surfNum].exposedFraction;
     722             :                 }
     723             :             } else {
     724           0 :                 ErrorsFound = true;
     725           0 :                 ShowSevereError(state,
     726           0 :                                 "Surface=\"" + Surfaces(surfNum).Name +
     727             :                                     "\", references a Foundation Outside Boundary Condition but there is no corresponding "
     728             :                                     "SURFACEPROPERTY:EXPOSEDFOUNDATIONPERIMETER object defined.");
     729             :             }
     730             : 
     731          46 :             Kiva::Polygon floorPolygon;
     732         115 :             for (std::size_t i = 0; i < surface.Vertex.size(); ++i) {
     733          92 :                 auto &v = surface.Vertex[i];
     734          92 :                 floorPolygon.outer().push_back(Kiva::Point(v.x, v.y));
     735          92 :                 if (!userSetExposedPerimeter) {
     736           0 :                     isExposedPerimeter.push_back(true);
     737             :                 }
     738             :             }
     739             : 
     740          23 :             Real64 totalPerimeter = 0.0;
     741         115 :             for (std::size_t i = 0; i < surface.Vertex.size(); ++i) {
     742             :                 std::size_t iNext;
     743          92 :                 if (i == surface.Vertex.size() - 1) {
     744          23 :                     iNext = 0;
     745             :                 } else {
     746          69 :                     iNext = i + 1;
     747             :                 }
     748          92 :                 auto &v = surface.Vertex[i];
     749          92 :                 auto &vNext = surface.Vertex[iNext];
     750          92 :                 totalPerimeter += distance(v, vNext);
     751             :             }
     752             : 
     753          23 :             if (useDetailedExposedPerimeter) {
     754          19 :                 Real64 total2DPerimeter = 0.0;
     755          19 :                 Real64 exposed2DPerimeter = 0.0;
     756          95 :                 for (std::size_t i = 0; i < floorPolygon.outer().size(); ++i) {
     757             :                     std::size_t iNext;
     758          76 :                     if (i == floorPolygon.outer().size() - 1) {
     759          19 :                         iNext = 0;
     760             :                     } else {
     761          57 :                         iNext = i + 1;
     762             :                     }
     763          76 :                     auto &p = floorPolygon.outer()[i];
     764          76 :                     auto &pNext = floorPolygon.outer()[iNext];
     765          76 :                     Real64 perim = Kiva::getDistance(p, pNext);
     766          76 :                     total2DPerimeter += perim;
     767          76 :                     if (isExposedPerimeter[i]) {
     768          28 :                         exposed2DPerimeter += perim;
     769             :                     } else {
     770          48 :                         exposed2DPerimeter += 0.0;
     771             :                     }
     772             :                 }
     773          19 :                 exposedFraction = std::min(exposed2DPerimeter / total2DPerimeter, 1.0);
     774             :             }
     775             : 
     776          23 :             Real64 totalExposedPerimeter = exposedFraction * totalPerimeter;
     777             : 
     778             :             // Remaining exposed perimeter will be alloted to each instance as appropriate
     779          23 :             Real64 remainingExposedPerimeter = totalExposedPerimeter;
     780             : 
     781             :             // Get combinations of wall constructions and wall heights -- each different
     782             :             // combination gets its own Kiva instance. Combination map points each set
     783             :             // of construction and wall height to the associated exposed perimeter and
     784             :             // list of wall surface numbers.
     785          46 :             std::map<std::pair<int, Real64>, WallGroup> combinationMap;
     786             : 
     787          23 :             if (!wallSurfaces.empty()) {
     788          18 :                 for (auto &wl : wallSurfaces) {
     789             : 
     790          15 :                     auto &v = Surfaces(wl).Vertex;
     791          15 :                     auto numVs = v.size();
     792             :                     // Enforce quadrilateralism
     793          15 :                     if (numVs > 4) {
     794           0 :                         ShowWarningError(state,
     795           0 :                                          "Foundation:Kiva=\"" + foundationInputs[surface.OSCPtr].name +
     796             :                                              "\", wall surfaces with more than four vertices referencing");
     797           0 :                         ShowContinueError(
     798             :                             state, "...Foundation Outside Boundary Conditions may not be interpreted correctly in the 2D finite difference model.");
     799           0 :                         ShowContinueError(state, format("Surface=\"{}\", has {} vertices.", Surfaces(wl).Name, numVs));
     800           0 :                         ShowContinueError(state,
     801             :                                           "Consider separating the wall into separate surfaces, each spanning from the floor slab to the top of "
     802             :                                           "the foundation wall.");
     803             :                     }
     804             : 
     805             :                     // get coplanar points with floor to determine perimeter
     806             :                     std::vector<int> coplanarPoints = Vectors::PointsInPlane(
     807          30 :                         Surfaces(surfNum).Vertex, Surfaces(surfNum).Sides, Surfaces(wl).Vertex, Surfaces(wl).Sides, ErrorsFound);
     808             : 
     809          15 :                     Real64 perimeter = 0.0;
     810             : 
     811             :                     // if there are two consecutive coplanar points, add the distance
     812             :                     // between them to the overall perimeter for this wall
     813          45 :                     for (std::size_t i = 0; i < coplanarPoints.size(); ++i) {
     814          30 :                         int p(coplanarPoints[i]);
     815          30 :                         int pC = p == (int)v.size() ? 1 : p + 1;                                             // next consecutive point
     816          30 :                         int p2 = i == coplanarPoints.size() - 1 ? coplanarPoints[0] : coplanarPoints[i + 1]; // next coplanar point
     817             : 
     818          30 :                         if (p2 == pC) { // if next coplanar point is the next consecutive point
     819          15 :                             perimeter += distance(v(p), v(p2));
     820             :                         }
     821             :                     }
     822             : 
     823          15 :                     if (perimeter == 0.0) {
     824           0 :                         ShowWarningError(state, "Foundation:Kiva=\"" + foundationInputs[surface.OSCPtr].name + "\".");
     825           0 :                         ShowContinueError(state, "   Wall Surface=\"" + Surfaces(wl).Name + "\", does not have any vertices that are");
     826           0 :                         ShowContinueError(state, "   coplanar with the corresponding Floor Surface=\"" + Surfaces(surfNum).Name + "\".");
     827           0 :                         ShowContinueError(state,
     828             :                                           "   Simulation will continue using the distance between the two lowest points in the wall for the "
     829             :                                           "interface distance.");
     830             : 
     831             :                         // sort vertices by Z-value
     832           0 :                         std::vector<int> zs;
     833           0 :                         for (std::size_t i = 0; i < numVs; ++i) {
     834           0 :                             zs.push_back(i);
     835             :                         }
     836           0 :                         sort(zs.begin(), zs.end(), [v](int a, int b) { return v[a].z < v[b].z; });
     837           0 :                         perimeter = distance(v[zs[0]], v[zs[1]]);
     838             :                     }
     839             : 
     840          15 :                     Real64 surfHeight = Surfaces(wl).get_average_height(state);
     841             :                     // round to avoid numerical precision differences
     842          15 :                     surfHeight = std::round((surfHeight)*1000.0) / 1000.0;
     843             : 
     844          15 :                     if (combinationMap.count({Surfaces(wl).Construction, surfHeight}) == 0) {
     845             :                         // create new combination
     846          12 :                         std::vector<int> walls = {wl};
     847           6 :                         combinationMap[{Surfaces(wl).Construction, surfHeight}] = WallGroup(perimeter, walls);
     848             :                     } else {
     849             :                         // add to existing combination
     850           9 :                         combinationMap[{Surfaces(wl).Construction, surfHeight}].exposedPerimeter += perimeter;
     851           9 :                         combinationMap[{Surfaces(wl).Construction, surfHeight}].wallIDs.push_back(wl);
     852             :                     }
     853             :                 }
     854             :             }
     855             : 
     856             :             // setup map to point floor surface to all related kiva instances
     857          46 :             Kiva::Aggregator floorAggregator(Kiva::Surface::ST_SLAB_CORE);
     858             : 
     859             :             // Loop through combinations and assign instances until there is no remaining exposed pereimeter
     860          23 :             bool assignKivaInstances = true;
     861          46 :             auto comb = combinationMap.begin();
     862          77 :             while (assignKivaInstances) {
     863             :                 int constructionNum;
     864             :                 Real64 wallHeight;
     865             :                 Real64 perimeter;
     866          54 :                 std::vector<int> wallIDs;
     867          27 :                 if (comb != combinationMap.end()) {
     868             :                     // Loop through wall combinations first
     869           6 :                     constructionNum = comb->first.first;
     870           6 :                     wallHeight = comb->first.second;
     871           6 :                     perimeter = comb->second.exposedPerimeter;
     872           6 :                     wallIDs = comb->second.wallIDs;
     873             :                 } else {
     874             :                     // Assign the remaining exposed perimeter to a slab instance
     875          21 :                     constructionNum = foundationInputs[surface.OSCPtr].wallConstructionIndex;
     876          21 :                     wallHeight = 0.0;
     877          21 :                     perimeter = remainingExposedPerimeter;
     878             :                 }
     879             : 
     880             :                 Real64 floorWeight;
     881             : 
     882          27 :                 if (totalExposedPerimeter > 0.001) {
     883          24 :                     floorWeight = perimeter / totalExposedPerimeter;
     884             :                 } else {
     885           3 :                     floorWeight = 1.0;
     886             :                 }
     887             : 
     888             :                 // Copy foundation input for this instance
     889          54 :                 Kiva::Foundation fnd = foundationInputs[surface.OSCPtr].foundation;
     890             : 
     891             :                 // Exposed Perimeter
     892          27 :                 fnd.useDetailedExposedPerimeter = useDetailedExposedPerimeter;
     893          27 :                 fnd.isExposedPerimeter = isExposedPerimeter;
     894          27 :                 fnd.exposedFraction = exposedFraction;
     895             : 
     896          27 :                 if (constructionNum > 0) {
     897           6 :                     auto &c = Constructs(constructionNum);
     898             : 
     899             :                     // Clear layers
     900           6 :                     fnd.wall.layers.clear();
     901             : 
     902             :                     // Push back construction's layers
     903          12 :                     for (int layer = 1; layer <= c.TotLayers; layer++) {
     904           6 :                         auto &mat = Materials(c.LayerPoint(layer));
     905           6 :                         if (mat.ROnly) {
     906           0 :                             ErrorsFound = true;
     907           0 :                             ShowSevereError(state, "Construction=\"" + c.Name + "\", constructions referenced by surfaces with a");
     908           0 :                             ShowContinueError(state, "\"Foundation\" Outside Boundary Condition must use only regular material objects");
     909           0 :                             ShowContinueError(state, "Material=\"" + mat.Name + "\", is not a regular material object");
     910           0 :                             return ErrorsFound;
     911             :                         }
     912             : 
     913           6 :                         Kiva::Layer tempLayer;
     914             : 
     915           6 :                         tempLayer.material = Kiva::Material(mat.Conductivity, mat.Density, mat.SpecHeat);
     916           6 :                         tempLayer.thickness = mat.Thickness;
     917             : 
     918           6 :                         fnd.wall.layers.push_back(tempLayer);
     919             :                     }
     920           6 :                     fnd.wall.interior.emissivity = Constructs(constructionNum).InsideAbsorpThermal;
     921           6 :                     fnd.wall.interior.absorptivity = Constructs(constructionNum).InsideAbsorpSolar;
     922           6 :                     fnd.wall.exterior.emissivity = Constructs(constructionNum).OutsideAbsorpThermal;
     923           6 :                     fnd.wall.exterior.absorptivity = Constructs(constructionNum).OutsideAbsorpSolar;
     924             :                 }
     925             : 
     926             :                 // Set slab construction
     927          67 :                 for (int i = 0; i < Constructs(surface.Construction).TotLayers; ++i) {
     928          40 :                     auto &mat = Materials(Constructs(surface.Construction).LayerPoint[i]);
     929          40 :                     if (mat.ROnly) {
     930           0 :                         ErrorsFound = true;
     931           0 :                         ShowSevereError(
     932           0 :                             state, "Construction=\"" + Constructs(surface.Construction).Name + "\", constructions referenced by surfaces with a");
     933           0 :                         ShowContinueError(state, "\"Foundation\" Outside Boundary Condition must use only regular material objects");
     934           0 :                         ShowContinueError(state, "Material=\"" + mat.Name + "\", is not a regular material object");
     935           0 :                         return ErrorsFound;
     936             :                     }
     937             : 
     938          40 :                     Kiva::Layer tempLayer;
     939             : 
     940          40 :                     tempLayer.material = Kiva::Material(mat.Conductivity, mat.Density, mat.SpecHeat);
     941          40 :                     tempLayer.thickness = mat.Thickness;
     942             : 
     943          40 :                     fnd.slab.layers.push_back(tempLayer);
     944             :                 }
     945             : 
     946          27 :                 fnd.slab.interior.emissivity = Constructs(surface.Construction).InsideAbsorpThermal;
     947          27 :                 fnd.slab.interior.absorptivity = Constructs(surface.Construction).InsideAbsorpSolar;
     948             : 
     949          27 :                 fnd.foundationDepth = wallHeight;
     950             : 
     951          27 :                 fnd.hasPerimeterSurface = false;
     952          27 :                 fnd.perimeterSurfaceWidth = 0.0;
     953             : 
     954             :                 // Add blocks
     955          27 :                 auto intHIns = foundationInputs[surface.OSCPtr].intHIns;
     956          27 :                 auto intVIns = foundationInputs[surface.OSCPtr].intVIns;
     957          27 :                 auto extHIns = foundationInputs[surface.OSCPtr].extHIns;
     958          27 :                 auto extVIns = foundationInputs[surface.OSCPtr].extVIns;
     959          27 :                 auto footing = foundationInputs[surface.OSCPtr].footing;
     960             : 
     961          27 :                 if (std::abs(intHIns.width) > 0.0) {
     962           7 :                     intHIns.z += fnd.foundationDepth + fnd.slab.totalWidth();
     963           7 :                     fnd.inputBlocks.push_back(intHIns);
     964             :                 }
     965          27 :                 if (std::abs(intVIns.width) > 0.0) {
     966           0 :                     fnd.inputBlocks.push_back(intVIns);
     967             :                 }
     968          27 :                 if (std::abs(extHIns.width) > 0.0) {
     969           0 :                     extHIns.z += fnd.wall.heightAboveGrade;
     970           0 :                     extHIns.x = fnd.wall.totalWidth();
     971           0 :                     fnd.inputBlocks.push_back(extHIns);
     972             :                 }
     973          27 :                 if (std::abs(extVIns.width) > 0.0) {
     974           8 :                     extVIns.x = fnd.wall.totalWidth();
     975           8 :                     fnd.inputBlocks.push_back(extVIns);
     976             :                 }
     977          27 :                 if (std::abs(footing.width) > 0.0) {
     978           0 :                     footing.z = fnd.foundationDepth + fnd.slab.totalWidth() + fnd.wall.depthBelowSlab;
     979           0 :                     footing.x = fnd.wall.totalWidth() / 2.0 - footing.width / 2.0;
     980           0 :                     fnd.inputBlocks.push_back(footing);
     981             :                 }
     982             : 
     983          27 :                 Real64 initDeepGroundDepth = fnd.deepGroundDepth;
     984          27 :                 fnd.deepGroundDepth = getDeepGroundDepth(fnd);
     985             : 
     986          27 :                 if (fnd.deepGroundDepth > initDeepGroundDepth) {
     987           0 :                     ShowWarningError(state,
     988           0 :                                      format("Foundation:Kiva=\"{}\", the autocalculated deep ground depth ({:.3T} m) is shallower than "
     989             :                                             "foundation construction elements ({:.3T} m)",
     990           0 :                                             foundationInputs[surface.OSCPtr].name,
     991             :                                             initDeepGroundDepth,
     992           0 :                                             fnd.deepGroundDepth - 1.0));
     993           0 :                     ShowContinueError(state,
     994           0 :                                       format("The deep ground depth will be set one meter below the lowest element ({:.3T} m)", fnd.deepGroundDepth));
     995             :                 }
     996             : 
     997             :                 // polygon
     998             : 
     999          27 :                 fnd.polygon = floorPolygon;
    1000             : 
    1001          54 :                 std::pair<EnergyPlusData *, std::string> contextPair{&state, format("Foundation:Kiva=\"{}\"", foundationInputs[surface.OSCPtr].name)};
    1002          27 :                 Kiva::setMessageCallback(kivaErrorCallback, &contextPair);
    1003             : 
    1004             :                 // point surface to associated ground instance(s)
    1005          27 :                 kivaInstances.emplace_back(state,
    1006             :                                            fnd,
    1007             :                                            surfNum,
    1008             :                                            wallIDs,
    1009             :                                            surface.Zone,
    1010          27 :                                            foundationInputs[surface.OSCPtr].assumedIndoorTemperature,
    1011             :                                            floorWeight,
    1012             :                                            constructionNum,
    1013          81 :                                            this);
    1014             : 
    1015             :                 // Floors can point to any number of foundation surfaces
    1016          27 :                 floorAggregator.add_instance(kivaInstances[inst].instance.ground.get(), floorWeight);
    1017             : 
    1018             :                 // Walls can only have one associated ground instance
    1019          42 :                 for (auto &wl : wallIDs) {
    1020          15 :                     surfaceMap[wl] = Kiva::Aggregator(Kiva::Surface::ST_WALL_INT);
    1021          15 :                     surfaceMap[wl].add_instance(kivaInstances[inst].instance.ground.get(), 1.0);
    1022             :                 }
    1023             : 
    1024             :                 // Increment instance counter
    1025          27 :                 inst++;
    1026             : 
    1027             :                 // Increment wall combinations iterator
    1028          27 :                 if (comb != combinationMap.end()) {
    1029           6 :                     comb++;
    1030             :                 }
    1031             : 
    1032          27 :                 remainingExposedPerimeter -= perimeter;
    1033             : 
    1034          27 :                 if (remainingExposedPerimeter < 0.001) {
    1035          23 :                     assignKivaInstances = false;
    1036          23 :                     if (remainingExposedPerimeter < -0.1) {
    1037           0 :                         ErrorsFound = true;
    1038           0 :                         ShowSevereError(state, "For Floor Surface=\"" + Surfaces(surfNum).Name + "\", the Wall surfaces referencing");
    1039           0 :                         ShowContinueError(state, "  the same Foundation:Kiva=\"" + foundationInputs[Surfaces(surfNum).OSCPtr].name + "\" have");
    1040           0 :                         ShowContinueError(state, "  a combined length greater than the exposed perimeter of the foundation.");
    1041           0 :                         ShowContinueError(state, "  Ensure that each Wall surface shares at least one edge with the corresponding");
    1042           0 :                         ShowContinueError(state, "  Floor surface.");
    1043             :                     }
    1044             :                 }
    1045             :             }
    1046             : 
    1047          23 :             surfaceMap[surfNum] = floorAggregator;
    1048             :         }
    1049             : 
    1050         490 :         surfNum++;
    1051             :     }
    1052             : 
    1053             :     // Loop through Foundation surfaces and make sure they are all assigned to an instance
    1054          47 :     for (auto surfNum : state.dataSurface->AllHTKivaSurfaceList) {
    1055          38 :         if (surfaceMap[surfNum].size() == 0) {
    1056           0 :             ErrorsFound = true;
    1057           0 :             ShowSevereError(state, "Surface=\"" + Surfaces(surfNum).Name + "\" has a 'Foundation' Outside Boundary Condition");
    1058           0 :             ShowContinueError(state, "  referencing Foundation:Kiva=\"" + foundationInputs[Surfaces(surfNum).OSCPtr].name + "\".");
    1059           0 :             if (Surfaces(surfNum).Class == DataSurfaces::SurfaceClass::Wall) {
    1060           0 :                 ShowContinueError(state, "  You must also reference Foundation:Kiva=\"" + foundationInputs[Surfaces(surfNum).OSCPtr].name + "\"");
    1061           0 :                 ShowContinueError(state,
    1062           0 :                                   "  in a floor surface within the same Zone=\"" + state.dataHeatBal->Zone(Surfaces(surfNum).Zone).Name + "\".");
    1063           0 :             } else if (Surfaces(surfNum).Class == DataSurfaces::SurfaceClass::Floor) {
    1064           0 :                 ShowContinueError(state, "  However, this floor was never assigned to a Kiva instance.");
    1065           0 :                 ShowContinueError(state, "  This should not occur for floor surfaces. Please report to EnergyPlus Development Team.");
    1066             :             } else {
    1067           0 :                 ShowContinueError(state, "  Only floor and wall surfaces are allowed to reference 'Foundation' Outside Boundary Conditions.");
    1068           0 :                 ShowContinueError(state, "  Surface=\"" + Surfaces(surfNum).Name + "\", is not a floor or wall.");
    1069             :             }
    1070             :         }
    1071             :     }
    1072             : 
    1073           9 :     print(state.files.eio,
    1074             :           "{}",
    1075             :           "! <Kiva Foundation Name>, Horizontal Cells, Vertical Cells, Total Cells, Total Exposed "
    1076             :           "Perimeter, Perimeter Fraction, Wall Height, Wall Construction, Floor Surface, Wall "
    1077           9 :           "Surface(s)\n");
    1078             : 
    1079          36 :     for (auto &kv : kivaInstances) {
    1080          27 :         auto grnd = kv.instance.ground.get();
    1081             : 
    1082          54 :         std::string constructionName;
    1083          27 :         if (kv.constructionNum <= 0) {
    1084          21 :             constructionName = "<Default Footing Wall Construction>";
    1085             :         } else {
    1086           6 :             constructionName = state.dataConstruction->Construct(kv.constructionNum).Name;
    1087             :         }
    1088             : 
    1089          54 :         std::string wallSurfaceString;
    1090          42 :         for (auto &wl : kv.wallSurfaces) {
    1091          15 :             wallSurfaceString += "," + state.dataSurface->Surface(wl).Name;
    1092             :         }
    1093             : 
    1094             :         static constexpr std::string_view fmt = "{},{},{},{},{:.2R},{:.2R},{:.2R},{},{}{}\n";
    1095         135 :         print(state.files.eio,
    1096             :               fmt,
    1097          27 :               foundationInputs[state.dataSurface->Surface(kv.floorSurface).OSCPtr].name,
    1098             :               grnd->nX,
    1099             :               grnd->nZ,
    1100          54 :               grnd->nX * grnd->nZ,
    1101          27 :               grnd->foundation.netPerimeter,
    1102             :               kv.floorWeight,
    1103          27 :               grnd->foundation.foundationDepth,
    1104             :               constructionName,
    1105          27 :               state.dataSurface->Surface(kv.floorSurface).Name,
    1106             :               wallSurfaceString);
    1107             :     }
    1108             : 
    1109           9 :     return ErrorsFound;
    1110             : }
    1111             : 
    1112          27 : Real64 KivaManager::getDeepGroundDepth(Kiva::Foundation fnd)
    1113             : {
    1114          27 :     Real64 totalDepthOfWallBelowGrade = fnd.wall.depthBelowSlab + (fnd.foundationDepth - fnd.wall.heightAboveGrade) + fnd.slab.totalWidth();
    1115          27 :     if (fnd.deepGroundDepth < totalDepthOfWallBelowGrade + 1.0) {
    1116           0 :         fnd.deepGroundDepth = totalDepthOfWallBelowGrade + 1.0;
    1117             :     }
    1118          42 :     for (auto &block : fnd.inputBlocks) {
    1119             :         // Change temporary zero depth indicators to default foundation depth
    1120          15 :         if (block.depth == 0.0) {
    1121           0 :             block.depth = fnd.foundationDepth;
    1122             :         }
    1123          15 :         if (settings.deepGroundBoundary == Settings::AUTO) {
    1124             :             // Ensure automatically set deep ground depth is at least 1 meter below lowest block
    1125          15 :             if (block.z + block.depth + 1.0 > fnd.deepGroundDepth) {
    1126           0 :                 fnd.deepGroundDepth = block.z + block.depth + 1.0;
    1127             :             }
    1128             :         }
    1129             :     }
    1130          27 :     return fnd.deepGroundDepth;
    1131             : }
    1132             : 
    1133          59 : void KivaManager::initKivaInstances(EnergyPlusData &state)
    1134             : {
    1135             :     // initialize temperatures at the beginning of run environment
    1136         260 :     for (auto &kv : kivaInstances) {
    1137             :         // Start with steady-state solution
    1138         201 :         kv.initGround(state, kivaWeather);
    1139             :     }
    1140          59 :     calcKivaSurfaceResults(state);
    1141          59 : }
    1142             : 
    1143         576 : void KivaManager::calcKivaInstances(EnergyPlusData &state)
    1144             : {
    1145             :     // calculate heat transfer through ground
    1146        2592 :     for (auto &kv : kivaInstances) {
    1147        2016 :         kv.setBoundaryConditions(state);
    1148        2016 :         kv.instance.calculate(timestep);
    1149        2016 :         kv.instance.calculate_surface_averages();
    1150             : #ifdef GROUND_PLOT
    1151             :         if (state.dataEnvrn->Month == 1 && state.dataEnvrn->DayOfMonth == 1 && state.dataGlobal->HourOfDay == 1 && state.dataGlobal->TimeStep == 1) {
    1152             :             kv.plotDomain(state);
    1153             :         }
    1154             : #endif
    1155             :     }
    1156             : 
    1157         576 :     calcKivaSurfaceResults(state);
    1158         576 : }
    1159             : 
    1160             : #ifdef GROUND_PLOT
    1161             : void KivaInstanceMap::plotDomain(EnergyPlusData &state)
    1162             : {
    1163             :     gp.createFrame(*instance.ground, format("{}/{} {}:00", state.dataEnvrn->Month, state.dataEnvrn->DayOfMonth, state.dataGlobal->HourOfDay));
    1164             : 
    1165             :     instance.ground->writeCSV(format("{}/{}.csv", debugDir, plotNum));
    1166             : 
    1167             :     plotNum++;
    1168             : }
    1169             : #endif
    1170             : 
    1171         635 : void KivaManager::calcKivaSurfaceResults(EnergyPlusData &state)
    1172             : {
    1173        3435 :     for (auto surfNum : state.dataSurface->AllHTKivaSurfaceList) {
    1174        5600 :         std::pair<EnergyPlusData *, std::string> contextPair{&state, format("Surface=\"{}\"", state.dataSurface->Surface(surfNum).Name)};
    1175        2800 :         Kiva::setMessageCallback(kivaErrorCallback, &contextPair);
    1176        2800 :         surfaceMap[surfNum].calc_weighted_results();
    1177        2800 :         state.dataHeatBalSurf->SurfHConvInt(surfNum) = state.dataSurfaceGeometry->kivaManager.surfaceMap[surfNum].results.hconv;
    1178             :     }
    1179         635 :     Kiva::setMessageCallback(kivaErrorCallback, nullptr);
    1180         635 : }
    1181             : 
    1182         771 : void KivaManager::defineDefaultFoundation(EnergyPlusData &state)
    1183             : {
    1184             : 
    1185        1542 :     Kiva::Foundation defFnd;
    1186             : 
    1187             :     // From settings
    1188         771 :     defFnd.soil = Kiva::Material(settings.soilK, settings.soilRho, settings.soilCp);
    1189         771 :     defFnd.grade.absorptivity = settings.groundSolarAbs;
    1190         771 :     defFnd.grade.emissivity = settings.groundThermalAbs;
    1191         771 :     defFnd.grade.roughness = settings.groundRoughness;
    1192         771 :     defFnd.farFieldWidth = settings.farFieldWidth;
    1193             : 
    1194         771 :     Real64 waterTableDepth = 0.1022 * state.dataEnvrn->Elevation;
    1195             : 
    1196         771 :     if (settings.deepGroundBoundary == Settings::AUTO) {
    1197         770 :         if (waterTableDepth <= 40.) {
    1198         680 :             defFnd.deepGroundDepth = waterTableDepth;
    1199         680 :             defFnd.deepGroundBoundary = Kiva::Foundation::DGB_FIXED_TEMPERATURE;
    1200             :         } else {
    1201          90 :             defFnd.deepGroundDepth = 40.;
    1202          90 :             defFnd.deepGroundBoundary = Kiva::Foundation::DGB_ZERO_FLUX;
    1203             :         }
    1204         770 :         if (!settings.autocalculateDeepGroundDepth) {
    1205           0 :             if (defFnd.deepGroundDepth != settings.deepGroundDepth) {
    1206           0 :                 ShowWarningError(state, "Foundation:Kiva:Settings, when Deep-Ground Boundary Condition is Autoselect,");
    1207           0 :                 ShowContinueError(state, format("the user-specified Deep-Ground Depth ({:.1R} m)", settings.deepGroundDepth));
    1208           0 :                 ShowContinueError(state, format("will be overridden with the Autoselected depth ({:.1R} m)", defFnd.deepGroundDepth));
    1209             :             }
    1210             :         }
    1211           1 :     } else if (settings.deepGroundBoundary == Settings::ZERO_FLUX) {
    1212           1 :         defFnd.deepGroundDepth = settings.deepGroundDepth;
    1213           1 :         defFnd.deepGroundBoundary = Kiva::Foundation::DGB_ZERO_FLUX;
    1214             :     } else { // if (settings.deepGroundBoundary == Settings::GROUNDWATER)
    1215           0 :         defFnd.deepGroundDepth = settings.deepGroundDepth;
    1216           0 :         defFnd.deepGroundBoundary = Kiva::Foundation::DGB_FIXED_TEMPERATURE;
    1217             :     }
    1218             : 
    1219         771 :     defFnd.wall.heightAboveGrade = 0.2; // m
    1220             : 
    1221         771 :     Kiva::Material concrete;
    1222         771 :     concrete.conductivity = 1.95; // W/m-K
    1223         771 :     concrete.density = 2400;      // kg/m3
    1224         771 :     concrete.specificHeat = 900;  // J/kg-K
    1225             : 
    1226         771 :     Kiva::Layer defaultFoundationWall;
    1227         771 :     defaultFoundationWall.thickness = 0.3; // m
    1228         771 :     defaultFoundationWall.material = concrete;
    1229             : 
    1230         771 :     defFnd.wall.layers.push_back(defaultFoundationWall);
    1231             : 
    1232         771 :     defFnd.wall.interior.emissivity = 0.9;
    1233         771 :     defFnd.wall.interior.absorptivity = 0.9;
    1234         771 :     defFnd.wall.exterior.emissivity = 0.9;
    1235         771 :     defFnd.wall.exterior.absorptivity = 0.9;
    1236             : 
    1237         771 :     defFnd.wall.depthBelowSlab = 0.0;
    1238             : 
    1239         771 :     defFnd.mesh.minCellDim = settings.minCellDim;
    1240         771 :     defFnd.mesh.maxNearGrowthCoeff = settings.maxGrowthCoeff;
    1241         771 :     defFnd.mesh.maxDepthGrowthCoeff = settings.maxGrowthCoeff;
    1242         771 :     defFnd.mesh.maxInteriorGrowthCoeff = settings.maxGrowthCoeff;
    1243         771 :     defFnd.mesh.maxExteriorGrowthCoeff = settings.maxGrowthCoeff;
    1244             : 
    1245         771 :     defaultFoundation.foundation = defFnd;
    1246         771 :     defaultFoundation.name = "<Default Foundation>";
    1247         771 :     defaultFoundation.assumedIndoorTemperature = -9999;
    1248         771 : }
    1249             : 
    1250           2 : void KivaManager::addDefaultFoundation()
    1251             : {
    1252           2 :     foundationInputs.push_back(defaultFoundation);
    1253           2 :     defaultIndex = static_cast<int>(foundationInputs.size() - 1u);
    1254           2 :     defaultAdded = true;
    1255           2 : }
    1256             : 
    1257          30 : int KivaManager::findFoundation(std::string const &name)
    1258             : {
    1259          30 :     int fndNum = 0;
    1260          30 :     for (auto &fnd : foundationInputs) {
    1261             :         // Check if foundation exists
    1262          30 :         if (fnd.name == name) {
    1263          30 :             return fndNum;
    1264             :         }
    1265           0 :         fndNum++;
    1266             :     }
    1267           0 :     return (int)foundationInputs.size();
    1268             : }
    1269             : 
    1270        2313 : } // namespace EnergyPlus::HeatBalanceKivaManager

Generated by: LCOV version 1.13