using Rugrid.Sdaid.Notation.Documents;
using Rugrid.Sdaid.Epas.Ntr.Backend.Calc;
namespace Rugrid.Sdaid.Epas.Ntr.ChangeRequests
{
/// <summary>
/// Запрос заказчика: показатель бытовых тепловыделений меняет смысл, единицу измерения и формулу
/// расчёта — вместо энергии за отопительный период считается удельная величина.
/// </summary>
public class DOC_CR_2026_08_28_InternalHeatGain : IChangeRequestDocument, IHasStructuralLinks, IFolder<NtrChangeRequestsFolder>
{
public SdaidLink[] Links => new[]
{
Rel.Uses<DOC_TtrCalc>(),
Rel.Uses<DOC_EeCalc>(),
Rel.Uses<DOC_CalcGraph>(),
Rel.Uses<Rugrid.Sdaid.Epas.Ntr.Domain.ConsumptionModel.DOC_LightingModel>(),
};
public static string _refName = "CR 2026-08-28: удельные бытовые теплопоступления";
public string Name => "Смена показателя бытовых тепловыделений на удельный";
public string Description =>
@"По запросу заказчика показатель «Энергия бытовых тепловыделений за отопительный период, тыс. кВт·ч» заменяется на «Удельные бытовые теплопоступления, Вт/м²»: меняются наименование, единица измерения и формула расчёта. Дальнейшее использование показателя в методике не меняется.";
public string Version => "0.1";
public string Status => "draft";
public string[] Comments => new string[0];
public static string Source = """"
## Источник (как есть)
Показатель Энергия бытовых тепловыделений за отопительный период, тыс. кВт*ч надо переименовать в
Удельные бытовые теплопоступления, Вт/м2. Меняется единица измерения и формула расчёта. Новая формула:
$$Q_{быт}=((90·n_p + 10+1000·P_{light})/A_h)·45 / 168, Вт/м^2$$
где:
- $P_{light}$ - установленная мощность осветительных приборов модели освещения, $кВт$.
дальнейшее использование показателя остаётся прежним, ничего не меняется
"""";
public static string Analysis = $$""""
## Разбор
Меняется секция методики {{SdaidAnchors.RefTo<TeInternalHeatGainEnergy>("te.internal_heat_gain_energy")}}
целиком: наименование показателя, единица измерения, формула и состав исходных данных.
**Обозначение меняется с $Q_{быт}$ на $q_{быт}$.** Показатель перестал быть энергией и стал
удельной величиной, а в методике заглавная $Q$ закреплена за энергией (ср. $Q_{рад}$ — энергия
теплопоступлений от солнечной радиации), строчная $q$ — за удельными характеристиками
($q_{норм}$, $q_{от}^{р}$). Обозначение $q_{быт}$ в методике свободно; с $k_{быт}$ оно не
путается — разные буквы и разные единицы измерения (Вт/м² против Вт/(м³·°C)).
**Что уходит из формулы.** Продолжительность отопительного периода $z_{ht}$ и среднее время
работы в сутки $t_{работы}$ (справочное значение справочника «Коэффициенты ТТР»). Показатель
перестаёт быть величиной «за отопительный период» и становится удельной мощностью, поэтому
правило валидации $z_{ht} \in [1; 365]$ в этой секции больше не нужно — сама характеристика
используется и валидируется в других секциях.
**Что приходит в формулу.** Установленная мощность осветительных приборов $P_{light}$, кВт —
расчётный параметр секции {{SdaidAnchors.RefTo<EeLightingModelTotals>("ee.lighting_model_totals")}}
раздела {{SdaidAnchors.RefTo<DOC_EeCalc>()}}; в домене это атрибут
«Установленная мощность, кВт» сущности
{{SdaidAnchors.RefTo<Rugrid.Sdaid.Epas.Ntr.Domain.ConsumptionModel.DOC_LightingModel>()}}.
**Это новая связь между разделами расчёта.** Сейчас ТЭ и ЭЭ считаются независимо: фасад расчёта
вызывает их по отдельности, и ни один параметр из ЭЭ в ТЭ не передаётся. Новая формула делает
раздел ТЭ зависимым от результата раздела ЭЭ — самое существенное последствие запроса.
**Следствие: граф расчёта становится общим.** До этого CR граф — {{SdaidAnchors.RefTo<DOC_CalcGraph>()}} —
покрывал только раздел ТЭ; у раздела ЭЭ графа не было. Межразделовая связь в методике при
этом уже есть: секция {{SdaidAnchors.RefTo<SummaryEeConsumptionKwh>("summary.ee_consumption_kwh")}}
берёт расчётные параметры и из ТЭ, и из ЭЭ — но записана она прозой и на графе не видна.
До сих пор раздельные графы были допустимы, потому что связь лежала в хвосте: ТЭ и ЭЭ — два
независимых поддерева, сходящиеся в своде. Новая формула вставляет дугу из ЭЭ в **середину**
графа ТЭ: у секции {{SdaidAnchors.RefTo<TeInternalHeatGainEnergy>("te.internal_heat_gain_energy")}}
появляется входящая дуга от узла, которого в графе ТЭ нет. Раздельный граф перестаёт быть
корректным описанием порядка расчёта.
Цикла не возникает: ни одна секция раздела ЭЭ не использует расчётные параметры ТЭ. Порядок
остаётся однозначным — ЭЭ, затем ТЭ, затем свод.
**Дальнейшее использование не меняется — это подтверждается размерностью.** Потребитель у
показателя один: {{SdaidAnchors.RefTo<TeSpecInternalHeatGainChar>("te.spec_internal_heat_gain_char")}},
$k_{быт} = Q_{быт}·A_h / (V_h·(t_{int} - t_{ext}))$. С новой единицей измерения $Вт/м^2$
произведение $Q_{быт}·A_h$ даёт Вт, а результат — $Вт/(м^3·°C)$, что совпадает с объявленной
единицей измерения $k_{быт}$. При старой единице ($тыс.кВт·ч$) размерность потребителя не
сходилась — смена показателя её как раз исправляет.
### Принятые решения
**Передача $P_{light}$ в раздел ТЭ — не открытый вопрос.** С общим графом расчёта дуга
$P_{light}$ ничем не отличается от любой другой межсекционной дуги: узел считается после
своих предшественников, значение передаётся на вход. Граница «ТЭ / ЭЭ» — это структура
реализации (два сервиса, два входных раздела), а не факт методики; методика знает только
секции и связи между ними. Поэтому в спецификации решать нечего, а в реализации решение
прямое: фасад расчёта считает ЭЭ до ТЭ и передаёт $P_{light}$ на вход секции.
**Граф расчёта достраивается до общего** (закрывает вопрос об объёме работы по графу).
Узлы разделов ЭЭ и свода добавлены в существующий документ, он переименован в
{{SdaidAnchors.RefTo<DOC_CalcGraph>()}}. Отдельный документ верхнего уровня с сохранением
графов по разделам отвергнут: один и тот же факт — дуга между секциями — оказался бы
записан в двух местах, что нарушает правило одного факта. Попутно снят висячий след:
в графе стояла пометка, что узлы ЭЭ перенесены в отдельный граф расчёта потребления
электроэнергии, — такого документа никогда не существовало.
**Ранее посчитанные модели не пересчитываются автоматически.** Значение показателя
изменится, пересчёт выполняет пользователь по своей инициативе. Массовая операция
пересчёта не разрабатывается.
**Формула переносится дословно, как дана заказчиком.** При разборе были заданы два вопроса,
оба закрыты подтверждением исходной формулировки:
- слагаемое `+ 10` в числителе. В прежней формуле площадь входила слагаемым $0{,}01·A_h$ —
10 Вт на квадратный метр; в новой числитель делится на $A_h$, и по этой логике слагаемое
ожидалось бы как $10·A_h$. Принята запись из запроса: `+ 10`;
- множители `45 / 168` — константы методики, в справочник «Коэффициенты ТТР» не выносятся.
Справочное значение $t_{работы}$ (среднее время работы в сутки) из формулы уходит.
**Поведение при отсутствии предшественника — тоже общее правило.** Если предшественник не
посчитан, узел не считается, и дальше по графу — то же самое. Это уже работающая семантика
расчёта (гранулярность отказа по узлам), новых правил доработка не вводит.
### Открытые вопросы
Нет.
### Смежная находка
**Гранулярность отказа на границе разделов грубее, чем на графе.** Внутри раздела отказ
локален: не посчитанный узел не мешает соседним ветвям. Но на стыке разделов результат
обнуляется целиком — если в разделе ЭЭ есть хотя бы одна ошибка, весь его результат
отбрасывается. До сих пор это ни на что не влияло, потому что ТЭ ничего у ЭЭ не спрашивал.
После доработки ошибка в любой части ЭЭ (например, в видах токоприёмников, не имеющих к
освещению отношения) лишит раздел ТЭ значения $P_{light}$ и оборвёт ветвь $q_{быт}$ →
$k_{быт}$ → $q_{от}^{р}$ вплоть до класса энергоэффективности. Правильное поведение —
брать $P_{light}$ по узлу графа, а не по разделу целиком; это правка фасада расчёта,
не методики.
"""";
public static string BlastRadius = $$""""
## Blast radius
**Спецификация:**
| Документ | Что меняется |
|---|---|
| {{SdaidAnchors.RefTo<TeInternalHeatGainEnergy>("te.internal_heat_gain_energy")}} | наименование секции, формула, исходные данные, правила валидации |
| {{SdaidAnchors.RefTo<TeSpecInternalHeatGainChar>("te.spec_internal_heat_gain_char")}} | описание параметра $Q_{быт}$: наименование и единица измерения |
| {{SdaidAnchors.RefTo<TtrSistemaOboznacheniy>("Система обозначений")}} | строка $Q_{быт}$ в перечне обозначений: обозначение, наименование, единица измерения |
| {{SdaidAnchors.RefTo<TtrIshodnyeDannyeDlyaRascheta>("Исходные данные для расчёта")}} | строка $t_{работы}$ (уходит из формулы, см. OQ-2) и строка «Коэффициенты в формуле $Q_{быт}$: 0,09; 0,01» |
| {{SdaidAnchors.RefTo<DOC_CalcGraph>()}} | становится общим графом расчёта: узлы разделов ЭЭ и свода, новая дуга $P_{light}$ из ЭЭ в ТЭ, подпись связи $Q_{быт}$ |
Переименование обозначения затрагивает те же документы — отдельной строкой не выделяется.
**Реализация (`src/Ntr.Calc`):** `InternalHeatGainEnergyInput` (состав параметров),
`InternalHeatGainEnergyCalculator` (формула и hint журнала расчёта),
`InternalHeatGainEnergyValidator` (правила), `TtrService` (передача $P_{light}$),
`CalcService` (порядок разделов), XML-doc `TEResult.InternalHeatGainEnergy` — единица измерения.
**Чего доработка НЕ касается.** Показатель $Q_{быт}$ наружу модуля расчёта не выходит: в
read-модель, API и на UI уходит только $k_{быт}$ (`SpecificInternalHeatGainsWtPerM3c`).
Поэтому миграций БД, правок контрактов API, swagger и интерфейса не требуется — доработка
укладывается в модуль расчёта. Единственное наблюдаемое пользователем изменение, помимо новых
значений показателей, — строка в журнале расчёта.
"""";
public static string Plan = """"
## План
1. ✔ Граф расчёта сведён в общий: {{SdaidAnchors.RefTo<DOC_CalcGraph>()}} — добавлены узлы
разделов ЭЭ и свода.
2. ✔ Правки спецификации по таблице blast radius; в изменённой секции проставлены `Status`
и `Comments` со ссылкой на этот CR.
3. Реализация в `Ntr.Calc` (включая порядок разделов и передачу $P_{light}$ по узлу графа).
4. Контрольный пример в `src/Ntr.Calc.Tests`: контрольные значения даёт заказчик или
пользователь — расчёт их тем же алгоритмом, который проверяется, ничего не доказывает.
5. Зафиксировать «Итог».
"""";
public static string Result = """"
## Итог
| Пункт | Статус |
|---|---|
| Общий граф расчёта | выполнено |
| Правки методики (наименование, обозначение, единица измерения, формула, исходные данные) | выполнено |
| Реализация в `Ntr.Calc` | выполнено |
| Контрольные примеры | ожидают контрольных значений заказчика |
Пересчёт ранее посчитанных моделей не выполняется — по решению заказчика пользователь
пересчитывает модели сам.
**Реализация.** Секция `te.internal_heat_gain_energy` пересчитана по новой формуле; из входа
секции ушла продолжительность отопительного периода, добавилась $P_{light}$. Фасад расчёта
считает раздел ЭЭ до раздела ТЭ и передаёт узловой результат `ee.lighting_model_totals`,
а не результат раздела: сводка освещения теперь вычисляется независимо от исхода ветви
токоприёмников (см. смежную находку). Тот же порядок восстановлен во втором месте, где
раздел ТЭ считается отдельно, — в правиле доступности модели при проверке энергопаспорта.
**Контрольные примеры.** Значения ниже по графу от $q_{быт}$ изменились, и подтвердить их
расчётом того же алгоритма нельзя. Ассерты, ставшие недействительными (k_быт, q_от^р, δq,
класс энергоэффективности, потребление ТЭ), отключены с пометкой `TODO (CR 2026-08-28)` до
получения контрольных значений от заказчика. Для контрольного примера «Гараж №1»
$k_{быт}$ изменился с 0,0736 до 0,0022 Вт/(м³·°C) — примерно в 33 раза меньше, что
смещает расчётную удельную характеристику и класс энергоэффективности.
"""";
}
}