LCOV - code coverage report
Current view: top level - EnergyPlus - HeatBalanceKivaManager.cc (source / functions) Coverage Total Hit
Test: lcov.output.filtered Lines: 31.2 % 663 207
Test Date: 2025-05-22 16:09:37 Functions: 52.4 % 21 11

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

Generated by: LCOV version 2.0-1