2. Модель расчёта CalcModel
Контракт модели расчёта: InputModel (единая модель ТЭ + ЭЭ), OutputModel (расчётные параметры МОП), CalcLog.
CalcModel {
context: {
idMap: [
{ refId: string, serviceId: uuid }
]
},
inputModel: InputModel,
outputModel: OutputModel,
calcLog: CalcLog
}
context.idMap — единообразный массив пар { refId, serviceId }, связывающий внутренние идентификаторы модуля расчёта с идентификаторами сущностей сервиса в domain-data.json.
Синглтоны (одна сущность на модель, refId — фиксированное well-known имя):
| refId | Сущность сервиса | Коллекция в domain-data.json |
|---|---|---|
consumptionModel |
МодельОбъектаПотребления | consumptionModels |
building |
Здание | buildings |
geometry |
Форма и геометрия | buildingGeometry |
walls |
Стены | walls |
windowModel |
Модель окон | windowModels |
doorModel |
Модель ворот | doorModels |
roof |
ВОК | roofs |
groundContact |
ЗКГ | groundContacts |
ventilation |
Вентиляция | ventilations |
operatingConditions |
Условия эксплуатации | operatingConditions |
heatingPeriod |
Условия ОП | heatingPeriodConditions |
lightingModel |
Модель освещения ЭЭ | lightingModels |
receiverModel |
Модель токоприёмников ЭЭ | receiverModels |
Коллекции (несколько элементов на модель, refId генерируется в PrepareInputModel):
| refId (шаблон) | Сущность сервиса | Коллекция в domain-data.json |
|---|---|---|
windowKind-N |
Вид остекления проёмов | windows |
doorKind-N |
Вид воротных проёмов | doors |
lightingKind-N |
Вид освещения | buildingLighting |
receiverKind-N |
Вид токоприёмника | buildingEEConsumers |
2.1 InputModel (единая модель ТЭ + ЭЭ)
Единое правило для PrepareInputModel: если в расчёт подставляется значение из справочника, передаются:
- код записи-источника (
*Code); - само значение в формате
ValueObject.
InputModel {
te: TeInputSection,
ee: EeInputSection
}
InputModel не содержит сервисных идентификаторов. Сущности внутри InputModel идентифицируются через refId (см. §2).
2.1.1 te: TeInputSection
TeInputSection {
geometry: {
buildingFormCode: string,
orientationSideACode: string,
buildingHeightM: ValueObject,
buildingFloorCount: ValueObject,
upperFloorLevelM: ValueObject,
heatedVolumeM3: ValueObject,
heatedAreaM2: ValueObject,
dimensionsM: {
A: ValueObject,
B: ValueObject,
C: ValueObject,
D: ValueObject,
E: ValueObject,
F: ValueObject,
G: ValueObject,
H: ValueObject
}
},
walls: {
material: {
materialCode: string,
thicknessMm: ValueObject,
thermalConductivity: ValueObject
},
mainInsulation: {
insulationCode: string,
thicknessMm: ValueObject,
thermalConductivity: ValueObject
},
additionalInsulation: {
insulationCode: string,
thicknessMm: ValueObject,
thermalConductivity: ValueObject
}
},
roof: {
roofTypeCode: string,
hasAttic: boolean,
ridgeLengthM: ValueObject | null,
customRoofAreaM2: ValueObject | null,
customAtticWallAreaM2: ValueObject | null,
floorLayer: {
materialCode: string,
thicknessMm: ValueObject,
thermalConductivity: ValueObject
},
floorInsulation: {
insulationCode: string,
thicknessMm: ValueObject,
thermalConductivity: ValueObject
},
roofInsulation: {
insulationCode: string,
thicknessMm: ValueObject,
thermalConductivity: ValueObject
}
},
groundContact: {
hasBasement: boolean,
basementCeilingHeightM: ValueObject | null,
basementFloorLevelM: ValueObject | null,
customGroundContactAreaM2: ValueObject | null
},
windows: {
items: [
{
refId: string,
openingAreaM2: ValueObject,
openingsBySide: {
A: ValueObject,
B: ValueObject,
C: ValueObject,
D: ValueObject,
E: ValueObject,
F: ValueObject,
G: ValueObject,
H: ValueObject
},
thermalResistance: ValueObject,
shadingCoefficient: ValueObject,
solarRadiationCoefficient: ValueObject,
airPermeabilityAt10Pa: ValueObject
}
]
},
doors: {
items: [
{
refId: string,
openingAreaM2: ValueObject,
openingsBySide: {
A: ValueObject,
B: ValueObject,
C: ValueObject,
D: ValueObject,
E: ValueObject,
F: ValueObject,
G: ValueObject,
H: ValueObject
},
thermalResistance: ValueObject,
airPermeabilityAt10Pa: ValueObject
}
]
},
ventilation: {
hasMechanicalVentilation: boolean,
hasRecuperator: boolean,
recuperatorEfficiency: ValueObject | null
},
operating: {
workplacesCount: ValueObject,
internalTemperatureC: ValueObject,
buildingPurposeCode: string,
airConsumptionM3PerPersonHour: ValueObject,
heatingRegulationTypeCode: string,
kReg: ValueObject,
energySupplyTypeCode: string
},
heatingPeriod: {
durationDays: ValueObject,
externalTemperatureC: ValueObject,
windSpeedMps: ValueObject,
latitudeDeg: ValueObject
},
solarRadiation: {
items: [
{
directionCode: string,
monthNumber: number,
radiation: ValueObject
}
]
},
normative: {
specificHeatConsumptionNorm: {
recordCode: string,
valueObject: ValueObject
},
energyEfficiencyClasses: [
{
code: string,
deviationMinPercent: ValueObject,
deviationMaxPercent: ValueObject
}
]
},
coefficients: {
surfaceThermalResistance: {
code: string,
valueObject: ValueObject
},
airSpecificHeat: {
code: string,
valueObject: ValueObject
},
reductionBuildingsAirVolume: {
code: string,
valueObject: ValueObject
},
workingTimePerDayH: {
code: string,
valueObject: ValueObject
},
basementZonesThermalResistance: {
I: {
code: string,
valueObject: ValueObject
},
II: {
code: string,
valueObject: ValueObject
},
III: {
code: string,
valueObject: ValueObject
},
IV: {
code: string,
valueObject: ValueObject
}
}
}
}
Число в te.solarRadiation.items[].radiation берётся только из поля value записи справочника (solarRadiationData / solarRadiationRecord в domain-schema.json). В корректном наполнении справочника однозначно задаётся кортеж (сторона света, нижняя граница широтного диапазона в градусах северной широты, номер месяца); в массиве items для выбранного диапазона широты здания не ожидается больше одного элемента с одной и той же парой (directionCode, monthNumber).
2.1.2 ee: EeInputSection
EeInputSection {
lighting: {
items: [
{
refId: string,
fixturesCount: ValueObject,
lampsPerFixture: ValueObject,
lampPowerW: ValueObject,
hoursPerDay: ValueObject,
daysPerYear: ValueObject
}
]
},
receivers: {
items: [
{
refId: string,
totalPowerKw: ValueObject,
hoursPerDay: ValueObject,
daysPerYear: ValueObject
}
]
}
}
2.2 OutputModel (расчётные параметры МОП)
OutputModel {
te: TeOutputSection,
ee: EeOutputSection,
totals: CommonTotals
}
2.2.1 te: TeOutputSection
Все расчётные поля ТЭ задаются в формате ValueObject.
TeOutputSection {
energyEfficiencyClass: {
classCode: string,
normativeSpecificCharacteristic: ValueObject,
calcSpecificCharacteristic: ValueObject,
deviationPercent: ValueObject
},
keBuilding: {
specHeatProtectChar: ValueObject,
areaM2: ValueObject,
thermalResistance: ValueObject,
heatLossEnergyThousandKwh: ValueObject,
heatLossPowerKw: ValueObject
},
keWalls: {
specHeatProtectChar: ValueObject,
areaM2: ValueObject,
thermalResistance: ValueObject,
heatLossEnergyThousandKwh: ValueObject,
heatLossPowerKw: ValueObject,
overlapThicknessBySideM: {
A: ValueObject, B: ValueObject, C: ValueObject, D: ValueObject,
E: ValueObject, F: ValueObject, G: ValueObject, H: ValueObject
},
innerPerimeterM: ValueObject,
innerSurfaceAreaM2: ValueObject
},
keWindows: {
specHeatProtectChar: ValueObject,
areaM2: ValueObject,
thermalResistance: ValueObject,
heatLossEnergyThousandKwh: ValueObject,
heatLossPowerKw: ValueObject,
airPermeabilityResistance: ValueObject,
branch: { formMethodSectionId: string, orientationMethodSectionId: string },
areaBySide: { A: ValueObject, B: ValueObject, C: ValueObject, D: ValueObject, E: ValueObject, F: ValueObject, G: ValueObject, H: ValueObject },
tempSidesS1ToS4: { s1: ValueObject, s2: ValueObject, s3: ValueObject, s4: ValueObject },
areaByDirection: { north: ValueObject, east: ValueObject, south: ValueObject, west: ValueObject },
solarRadiation: {
byDirection: [
{ directionCode: string, value: ValueObject }
]
},
items: [{
refId: string,
specHeatProtectChar: ValueObject,
areaM2: ValueObject,
thermalResistance: ValueObject,
heatLossEnergyThousandKwh: ValueObject,
heatLossPowerKw: ValueObject,
airPermeabilityResistance: ValueObject,
infiltrationAirKgH: ValueObject
}]
},
keDoors: {
specHeatProtectChar: ValueObject,
areaM2: ValueObject,
thermalResistance: ValueObject,
heatLossEnergyThousandKwh: ValueObject,
heatLossPowerKw: ValueObject,
airPermeabilityResistance: ValueObject,
branch: { formMethodSectionId: string, orientationMethodSectionId: string },
areaBySide: { A: ValueObject, B: ValueObject, C: ValueObject, D: ValueObject, E: ValueObject, F: ValueObject, G: ValueObject, H: ValueObject },
tempSidesS1ToS4: { s1: ValueObject, s2: ValueObject, s3: ValueObject, s4: ValueObject },
areaByDirection: { north: ValueObject, east: ValueObject, south: ValueObject, west: ValueObject },
items: [{
refId: string,
specHeatProtectChar: ValueObject,
areaM2: ValueObject,
thermalResistance: ValueObject,
heatLossEnergyThousandKwh: ValueObject,
heatLossPowerKw: ValueObject,
airPermeabilityResistance: ValueObject,
infiltrationAirKgH: ValueObject
}]
},
keUpperEnclosures: {
specHeatProtectChar: ValueObject,
areaM2: ValueObject,
atticWallAreaM2: ValueObject,
roofCoverAreaM2: ValueObject,
thermalResistance: ValueObject,
heatLossEnergyThousandKwh: ValueObject,
heatLossPowerKw: ValueObject,
branch: { atticWallMethodSectionId: string, roofCoverMethodSectionId: string }
},
keGroundContact: {
specHeatProtectChar: ValueObject,
areaM2: ValueObject,
basementWallAreaM2: ValueObject,
floorAreaM2: ValueObject,
thermalResistance: ValueObject,
heatLossEnergyThousandKwh: ValueObject,
heatLossPowerKw: ValueObject,
branch: { basementWallMethodSectionId: string, floorAreaMethodSectionId: string, areaMethodSectionId: string, zonesFormMethodSectionId: string },
zonesAreaM2: { I: ValueObject, II: ValueObject, III: ValueObject, IV: ValueObject },
zoneSideLengths: object
},
solarRadiation: {
byDirection: [
{ directionCode: string, value: ValueObject }
]
},
ventilation: {
specVentChar: ValueObject,
heatLossEnergyThousandKwh: ValueObject,
heatLossPowerKw: ValueObject,
infiltrationAirMassTotal: ValueObject,
supplyAirFlowM3H: ValueObject,
ventilationHours: ValueObject,
recuperatorEfficiency: ValueObject,
infiltrationAirKgH: ValueObject,
infiltrationHours: ValueObject,
pressureDifferencePa: ValueObject,
airWeightOutside: ValueObject,
airWeightInside: ValueObject,
branch: { ventHoursMethodSectionId: string, recuperatorMethodSectionId: string, infiltrationHoursMethodSectionId: string }
},
heatGains: {
specHeatGainsChar: ValueObject,
heatGainsEnergyTotal: ValueObject,
heatGainsPowerTotal: ValueObject,
specDomesticHeatGainsChar: ValueObject,
domesticHeatGainsEnergy: ValueObject,
domesticHeatGainsPower: ValueObject,
specSolarHeatGainsChar: ValueObject,
solarHeatGainsEnergy: ValueObject,
solarHeatGainsPower: ValueObject,
coefHeatGainsEfficiency: ValueObject
},
heatingPeriod: {
degreeDays: ValueObject
}
}
2.2.2 ee: EeOutputSection
EeOutputSection {
lighting: {
items: [{ refId: string, installedPowerKw: ValueObject, annualConsumptionThousandKwh: ValueObject }],
installedPowerKw: ValueObject,
annualConsumptionThousandKwh: ValueObject
},
receivers: {
items: [{ refId: string, installedPowerKw: ValueObject, annualConsumptionThousandKwh: ValueObject }],
installedPowerKw: ValueObject,
annualConsumptionThousandKwh: ValueObject
},
subtotal: {
installedPowerKw: ValueObject,
annualConsumptionThousandKwh: ValueObject
}
}
2.2.3 totals: CommonTotals
CommonTotals {
eeWithoutHeating: {
installedPowerKw: ValueObject,
annualConsumptionThousandKwh: ValueObject
},
electricHeating: {
enabled: boolean,
annualConsumptionThousandKwh: ValueObject
},
eeTotal: {
annualConsumptionThousandKwh: ValueObject
},
teTotal: {
annualConsumptionThousandKwh: ValueObject
}
}
Правила агрегации итогов не дублируются в этом документе и задаются только в методиках:
ttr-service/domain/ttr-calc.mdttr-service/domain/ee_calc.md
2.3 CalcLog
2.3.1 Контракт CalcLog и записей CalcLogEntry
Оркестратор ModelCalc формирует журнал шагов расчёта. Каждый шаг — одна запись CalcLogEntry на вызов одной функции расчёта (functionId).
CalcLog {
startedAt: string,
finishedAt: string,
records: CalcLogEntry[],
errorsCount: ValueObject,
warningsCount: ValueObject
}
Структура CalcLogEntry одинакова для любого статуса:
CalcLogEntry {
functionId: string,
methodSectionId: string,
status: "ok" | "missing_input" | "error" | "not_found",
inputs: object,
outputs: object,
formulaTemplate: string,
formulaSubstitution: object,
missingInputs: string[]
}
inputs/outputs— объекты с привязкой полей к обозначениям методики и/или путямInputModel/OutputModel(как в карточах §4.3).formulaTemplate— шаблон формулы (в т.ч. LaTeX), с плейсхолдерами под подстановку значений изformulaSubstitution.formulaSubstitution— словарь «символ → подставленное значение» (число, строка с единицами, или"-"при отсутствии входа).
Правила формирования formulaSubstitution:
- заполняется на основе карточки функции (§4.3): обозначения берутся из
Входные параметрыи/илиПравило расчёта; - должен содержать значения всех входных параметров функции;
- для каждого параметра подставляется фактическое значение или
"-"при отсутствии значения; - если входной параметр — массив
X, используется форматX[key] = value; - если элементов массива
<= 10, указываются все элементы; - если элементов массива
> 10, указываются первые 10 элементов и затем"...".
Правила при status = missing_input:
- функция не выполняется;
inputsиoutputsв записи остаются (не очищаются);- в
formulaSubstitutionдля отсутствующих параметров подставляется"-"; missingInputsсодержит список путей параметров, из‑за которых шаг не выполнен.
2.3.2 Подсказка к значению расчётного показателя (UI)
Для отображаемого в интерфейсе значения поля OutputModel с путём outputPath:
- Найти запись журнала: выбрать
CalcLogEntryизrecords, в которой вoutputsприсутствует расчётный результат дляoutputPath(или согласованное с оркестратором правило «последняя запись, записавшая этот путь»). Если таких несколько (агрегация), UI следует правилам продукта, зафиксированным в отдельном требовании к экрану. - Собрать текст подсказки:
- взять
formulaTemplate; - подставить значения из
formulaSubstitutionвместо символов (включая отображение"-"для пропусков); - при необходимости добавить
methodSectionId/ ссылку на фрагмент методики (ТЭ или ЭЭ).
- взять
- При
missing_input: показать, что значение не получено, и вывестиmissingInputs(через запятую или списком), без выдумывания чисел в UI.
Структурные связи
Используется
- Документ Маппинг доменных данных → InputModel (ТЭ)
- Документ Маппинг SummaryResult → доменные сущности
- Документ Маппинг TEResult → доменные сущности
- Запрос на изменение Обработка источников климатических данных
- Запрос на изменение Расчёт площади ВОК для четырёхскатных крыш
- Прикладной сервис
- Прикладной сервис Расчёт по электричеству (ЭЭ)
- Прикладной сервис Теплотехнический расчёт