• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

SPF-OST / pytrnsys_gui / 11576810878

29 Oct 2024 03:09PM UTC coverage: 67.508% (-0.08%) from 67.591%
11576810878

push

github

web-flow
Merge pull request #564 from SPF-OST/560-black-change-line-length-to-pep8-standard-of-79-and-check-ci-reaction

changed line length in black to 79

1054 of 1475 new or added lines in 174 files covered. (71.46%)

150 existing lines in 74 files now uncovered.

10399 of 15404 relevant lines covered (67.51%)

0.68 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

97.42
/trnsysGUI/Export.py
1
# pylint: skip-file
2
import dataclasses as _dc
1✔
3
import logging as _log
1✔
4
import typing as _tp
1✔
5

6
import jinja2 as _jinja
1✔
7

8
import trnsysGUI.TVentil as _tventil
1✔
9
import trnsysGUI.connection.connectionBase as _cb
1✔
10
import trnsysGUI.connection.doublePipeConnection as _dpc
1✔
11
import trnsysGUI.connection.hydraulicExport.doublePipe.getDoublePipeEnergyBalanceEquations as _gdpebe
1✔
12
import trnsysGUI.connection.names as _cnames
1✔
13
import trnsysGUI.connection.singlePipeConnection as _spc
1✔
14
import trnsysGUI.connection.singlePipeDefaultValues as _spdValues
1✔
15
import trnsysGUI.globalNames as _gnames
1✔
16
import trnsysGUI.hydraulicLoops.model as _hlm
1✔
17
import trnsysGUI.hydraulicLoops.names as _lnames
1✔
18
import trnsysGUI.internalPiping as _ip
1✔
19
import trnsysGUI.massFlowSolver.export as _mfse
1✔
20
import trnsysGUI.massFlowSolver.globalNetwork as _gn
1✔
21
import trnsysGUI.massFlowSolver.names as _mnames
1✔
22
import trnsysGUI.massFlowSolver.networkModel as _mfn
1✔
23
import trnsysGUI.temperatures as _temps
1✔
24
import trnsysGUI.temperatures.hydraulic as _thyd
1✔
25

26
_UNUSED_INDEX = 0
1✔
27

28

29
@_dc.dataclass
1✔
30
class _SerializedNode:
1✔
31
    name: str
1✔
32
    index: int
1✔
33
    nodeType: int
1✔
34
    neighborIndexes: _tp.Tuple[int, int, int]
1✔
35

36

37
class Export:
1✔
38
    def __init__(
1✔
39
        self,
40
        diagramName: str,
41
        hasInternalPipings: _tp.Sequence[_ip.HasInternalPiping],
42
        hydraulicLoops: _tp.Sequence[_hlm.HydraulicLoop],
43
        fluids: _tp.Sequence[_hlm.Fluid],
44
        logger: _log.Logger,
45
        editor,
46
    ) -> None:
47
        self._hydraulicLoops = hydraulicLoops
1✔
48
        self._fluids = fluids
1✔
49
        self._diagramName = diagramName
1✔
50
        self._hasInternalPipings = hasInternalPipings
1✔
51
        self.logger = logger
1✔
52
        self._editor = editor
1✔
53

54
        self.maxChar = 20
1✔
55

56
        o: _ip.HasInternalPiping
57
        nodes = [
1✔
58
            n
59
            for c in self._hasInternalPipings
60
            for n in c.getInternalPiping().nodes
61
        ]
62
        self.lineNumOfPar = len(nodes)
1✔
63

64
        self.numOfPar = 4 * self.lineNumOfPar + 1
1✔
65

66
    def exportBlackBox(self, exportTo="ddck"):
1✔
67
        f = "*** Black box component temperatures" + "\n"
1✔
68
        equationNr = 0
1✔
69

70
        for t in self._hasInternalPipings:
1✔
71
            if not t.shallRenameOutputTemperaturesInHydraulicFile():
1✔
72
                continue
1✔
73

74
            equations = _thyd.export(t)
1✔
75

76
            for equation in equations:
1✔
77
                f += equation + "\n"
1✔
78
            equationNr += len(equations)
1✔
79

80
        if exportTo == "mfs":
1✔
81
            lines = f.split("\n")
1✔
82
            f = ""
1✔
83
            for i in range(len(lines)):
1✔
84
                if "=" in lines[i]:
1✔
85
                    lines[i] = lines[i].split("=")[0] + "=1"
1✔
86
                f += lines[i] + "\n"
1✔
87

88
        if equationNr == 0:
1✔
89
            f = ""
1✔
90
        else:
91
            f = "\nEQUATIONS " + str(equationNr) + "\n" + f + "\n"
1✔
92

93
        problemEncountered = False
1✔
94
        return problemEncountered, f
1✔
95

96
    def exportSinglePipeParameters(self) -> str:
1✔
97
        doesHydraulicContainSinglePipes = any(
1✔
98
            isinstance(obj, _spc.SinglePipeConnection)
99
            for obj in self._hasInternalPipings
100
        )
101

102
        if not doesHydraulicContainSinglePipes:
1✔
103
            return ""
×
104

105
        return f"""\
1✔
106
*** Default global PARAMETERS for single pipes
107
CONSTANTS 1
108
{_gnames.SinglePipes.INITIAL_TEMPERATURE} = 20
109

110
"""
111

112
    def exportDoublePipeParameters(self, exportTo="ddck"):
1✔
113
        doesHydraulicContainDoublePipes = any(
1✔
114
            isinstance(obj, _dpc.DoublePipeConnection)
115
            for obj in self._hasInternalPipings
116
        )
117

118
        if not doesHydraulicContainDoublePipes:
1✔
119
            return ""
1✔
120

121
        constantsBlock = f"""\
1✔
122
*** Default global PARAMETERS for double pipes ***
123
CONSTANTS 25
124

125
****** Pipe and soil properties ******
126
{_gnames.DoublePipes.REFERENCE_LENGTH} = 579.404 ! Length of buried pipe in m
127
dpDiamIn = 0.4028 ! Inner diameter of pipes in m
128
dpDiamOut = 0.429 ! Outer diameter of pipes in m
129
dpLambda = 175 ! Thermal conductivity of pipe material, kJ/(h*m*K)
130
dpDepth = 1.8 ! Buried pipe depth in m
131
dpDiamCase = 2 ! Diameter of casing material in m
132
dpLambdaFill = 7  ! Thermal conductivity of fill insulation in kJ/hr.m.K
133
dpDistPtoP = 0.55  ! Center-to-center pipe spacing in m
134
dpLambdaGap = 1.44  ! Thermal conductivity of gap material in kJ/hr.m.K (gravel)
135
dpGapThick = 0  ! Gap thickness in m
136

137
****** Fluid properties ******
138
dpRhoFlu = 1000.0 ! Density of fluid, kg/m^3
139
dpLambdaFl = LamWat*3.6  ! Thermal conductivity of fluid in kJ/hr.m.K
140
dpCpFl = 4.19 ! Specific heat of fluid, kJ/kg.K
141
dpViscFl = 3.078  ! Viscosity of fluid in kg/m.hr
142

143
****** Initial conditions ******
144
{_gnames.DoublePipes.INITIAL_HOT_TEMPERATURE} = 15  ! Initial fluid temperature - pipe 1 in degrees celsius
145
{_gnames.DoublePipes.INITIAL_COLD_TEMPERATURE}  = 10  ! Initial fluid temperature - pipe 2 in degrees celsius
146

147
****** Soil's thermal properties ******
148
dpLamdaSl = 8.64  ! Thermal conductivity of soil in kJ/hr.m.K
149
dpRhoSl = 1800  ! Density of soil in kg/m^3
150
dpCpSl = 1.0  ! Specific heat of soil in kJ/kg.K
151

152
****** Definition of nodes ******
153
{_gnames.DoublePipes.N_AXIAL_SOIL_NODES_AT_REFERENCE_LENGTH} = 10
154
{_gnames.DoublePipes.FLUID_TO_SOIL_NODES_RATIO} = 1
155
dpNrSlRad = 10  ! Number of radial soil nodes
156
dpSoilThickness = 0.5  ! Thickness of soil around the gravel considered in the model in m
157
dpRadNdDist = dpSoilThickness/dpNrSlRad ! Radial distance of any node in m
158
{_gnames.DoublePipes.N_CIRCUMFERENTIAL_SOIL_NODES} = 4  ! Number of circumferential soil nodes
159
"""
160
        massFlowSolverConstantsBlock = """\
1✔
161
CONSTANTS 3
162
TambAvg = 7.96 ! Average surface temperature in degrees celsius
163
dTambAmpl = 13.32 ! Amplitude of surface temperature in n degrees celsius
164
ddTcwOffset = 36 ! Days of minimum surface temperature
165
"""
166
        if exportTo == "ddck":
1✔
167
            return constantsBlock + "\n"
1✔
168
        elif exportTo == "mfs":
1✔
169
            return (
1✔
170
                constantsBlock + "\n\n" + massFlowSolverConstantsBlock + "\n"
171
            )
172
        else:
173
            raise ValueError("Unknown value for `exportTo`", exportTo)
×
174

175
    def exportMassFlows(self):  # What the controller should give
1✔
176
        f = "*** Massflowrates" + "\n"
1✔
177
        equationNr = 0
1✔
178

179
        for t in self._hasInternalPipings:
1✔
180
            f += t.exportMassFlows()[0]
1✔
181
            equationNr += t.exportMassFlows()[1]
1✔
182

183
        if equationNr == 0:
1✔
184
            f = ""
×
185
        else:
186
            f = "EQUATIONS " + str(equationNr) + "\n" + f + "\n"
1✔
187

188
        return f
1✔
189

190
    def exportDivSetting(self, unit):
1✔
191
        """
192
        :param unit: the index of the previous unit number used.
193
        :return:
194
        """
195
        f = ""
1✔
196

197
        nUnit = unit
1✔
198
        constants = 0
1✔
199
        f2 = ""
1✔
200
        for t in self._hasInternalPipings:
1✔
201
            f2 += t.exportDivSetting1()[0]
1✔
202
            constants += t.exportDivSetting1()[1]
1✔
203

204
        if constants > 0:
1✔
205
            f = "CONSTANTS " + str(constants) + "\n"
1✔
206
            f += f2 + "\n"
1✔
207

208
        for t in self._hasInternalPipings:
1✔
209
            res = t.exportDivSetting2(nUnit)
1✔
210
            f += res[0]
1✔
211
            nUnit = res[1]
1✔
212

213
        return f
1✔
214

215
    def exportParametersFlowSolver(
1✔
216
        self, simulationUnit: int, simulationType: int, descConnLength: int
217
    ) -> str:
218
        hasInternalPipings = []
1✔
219
        for hasInternalPiping in self._hasInternalPipings:
1✔
220
            noHydraulicConnection = (
1✔
221
                not isinstance(hasInternalPiping, _cb.ConnectionBase)
222
                and not hasInternalPiping.outputs  # type: ignore[attr-defined]
223
                and not hasInternalPiping.inputs  # type: ignore[attr-defined]
224
            )
225

226
            if noHydraulicConnection:
1✔
227
                continue
×
228

229
            hasInternalPipings.append(hasInternalPiping)
1✔
230

231
        serializedNodes = self._getSerializedNodes(hasInternalPipings)
1✔
232

233
        lines = []
1✔
234
        for serializedNode in serializedNodes:
1✔
235
            neighborIndexes = serializedNode.neighborIndexes
1✔
236

237
            firstColumn = f"{neighborIndexes[0]} {neighborIndexes[1]} {neighborIndexes[2]} {serializedNode.nodeType} "
1✔
238
            secondColumn = f"!{serializedNode.index} : {serializedNode.name}"
1✔
239

240
            line = f"{firstColumn.ljust(descConnLength)}{secondColumn}"
1✔
241

242
            lines.append(line)
1✔
243

244
        jointLines = "\n".join(lines)
1✔
245

246
        nToleranceParameters = 3
1✔
247

248
        resultText = f"""\
1✔
249
UNIT {simulationUnit} TYPE {simulationType}
250
PARAMETERS {nToleranceParameters + 1 + len(serializedNodes) * 4}
251
{_gnames.MassFlowSolver.ABSOLUTE_TOLERANCE}
252
{_gnames.MassFlowSolver.RELATIVE_TOLERANCE}
253
{_gnames.MassFlowSolver.SWITCHING_THRESHOLD}
254
{len(serializedNodes)}
255
{jointLines}
256
"""
257

258
        return resultText
1✔
259

260
    @classmethod
1✔
261
    def _getSerializedNodes(
1✔
262
        cls,
263
        hasInternalPipings: _tp.Sequence[_ip.HasInternalPiping],
264
    ) -> _tp.Sequence[_SerializedNode]:
265
        globalNetwork = _gn.getGlobalNetwork(hasInternalPipings)
1✔
266
        internalPortItemToExternalRealNode = (
1✔
267
            globalNetwork.internalPortItemToExternalNode
268
        )
269

270
        nodesToIndex = {
1✔
271
            nwp.node: i
272
            for i, nwp in enumerate(globalNetwork.nodesWithParent, start=1)
273
        }
274

275
        serializedNodes = []
1✔
276
        for index, nodeWithParent in enumerate(
1✔
277
            globalNetwork.nodesWithParent, start=1
278
        ):
279
            node = nodeWithParent.node
1✔
280
            parent = nodeWithParent.parent
1✔
281

282
            neighborsAndUnusedIndexes = cls._getNeighborAndUnusedIndexes(
1✔
283
                node, nodesToIndex, internalPortItemToExternalRealNode
284
            )
285

286
            displayName = parent.getDisplayName()
1✔
287
            nodeNameOrEmpty = node.name or ""
1✔
288
            fullName = f"{displayName}{nodeNameOrEmpty}"
1✔
289
            serializedNode = _SerializedNode(
1✔
290
                fullName, index, node.getNodeType(), neighborsAndUnusedIndexes
291
            )
292

293
            serializedNodes.append(serializedNode)
1✔
294

295
        return serializedNodes
1✔
296

297
    @staticmethod
1✔
298
    def _getNeighborAndUnusedIndexes(
1✔
299
        realNode: _mfn.Node,
300
        realNodesToIndex: _tp.Mapping[_mfn.Node, int],
301
        internalPortItemToExternalRealNode: _tp.Mapping[
302
            _mfn.PortItem, _mfn.Node
303
        ],
304
    ) -> _tp.Tuple[int, int, int]:
305
        neighborIndexes = []
1✔
306
        for neighbor in realNode.getPortItems():
1✔
307
            if isinstance(neighbor, _mfn.Node):
1✔
308
                neighborIndex = realNodesToIndex[neighbor]
×
309
            elif isinstance(neighbor, _mfn.PortItem):
1✔
310
                externalRealNode = internalPortItemToExternalRealNode[neighbor]
1✔
311
                neighborIndex = realNodesToIndex[externalRealNode]
1✔
312
            else:
313
                raise AssertionError("Can't get here.")
×
314

315
            neighborIndexes.append(neighborIndex)
1✔
316

317
        nNeighborIndexes = len(neighborIndexes)
1✔
318

319
        assert nNeighborIndexes > 0
1✔
320
        neighborAndUnusedIndexes = [
1✔
321
            neighborIndexes[0],
322
            _UNUSED_INDEX,
323
            _UNUSED_INDEX,
324
        ]
325

326
        if nNeighborIndexes > 1:
1✔
327
            neighborAndUnusedIndexes[1] = neighborIndexes[1]
1✔
328

329
        if nNeighborIndexes > 2:
1✔
330
            neighborAndUnusedIndexes[2] = neighborIndexes[2]
1✔
331

332
        return (
1✔
333
            neighborAndUnusedIndexes[0],
334
            neighborAndUnusedIndexes[1],
335
            neighborAndUnusedIndexes[2],
336
        )
337

338
    def exportInputsFlowSolver(self):
1✔
339
        f = ""
1✔
340
        f += "INPUTS " + str(self.lineNumOfPar) + "! for Type 9351\n"
1✔
341

342
        numberOfInputs = 0
1✔
343

344
        counter = 0
1✔
345
        for t in self._hasInternalPipings:
1✔
346
            res = _mfse.exportInputsFlowSolver(t)
1✔
347
            f += res[0]
1✔
348
            counter += res[
1✔
349
                1
350
            ]  # DC this is a very strange way to print values, I would like to have 10 values per line and the fact that two go together makes it complicated
351
            numberOfInputs += res[1]
1✔
352

353
            if counter > 9 or t == self._hasInternalPipings[-1]:
1✔
354
                f += "\n"
1✔
355
                counter = 0
1✔
356

357
        f += "*** Initial Inputs\n"
1✔
358
        counter = 0
1✔
359
        for _ in range(numberOfInputs):
1✔
360
            f += "0 "
1✔
361
            counter += 1
1✔
362
            # DC if we use 10 per line its way easier to count and see if it is well done.
363
            # Above should be the same but some elements give two at once and depending if they are at then end of the line 11 values show up
364
            if counter > 9:
1✔
365
                f += "\n"
1✔
366
                counter = 0
1✔
367

368
        f += "\n\n"
1✔
369

370
        return f
1✔
371

372
    def exportOutputsFlowSolver(self, simulationUnit):
1✔
373
        f = ""
1✔
374

375
        abc = "ABC"
1✔
376

377
        prefix = "Mfr"
1✔
378
        equationNumber = 1
1✔
379
        nEqUsed = 1  # DC
1✔
380

381
        tot = ""
1✔
382

383
        for t in self._hasInternalPipings:
1✔
384
            noHydraulicConnection = (
1✔
385
                not isinstance(t, _cb.ConnectionBase)
386
                and not t.outputs
387
                and not t.inputs
388
            )
389

390
            if noHydraulicConnection:
1✔
391
                continue
×
392
            else:
393
                res = _mfse.exportOutputsFlowSolver(
1✔
394
                    t, equationNumber, simulationUnit
395
                )
396
                tot += res[0]
1✔
397
                equationNumber = res[1]
1✔
398
                nEqUsed += res[2]
1✔
399

400
        head = (
1✔
401
            "EQUATIONS {0}        ! Output up to three (A,B,C) mass flow rates of each component, positive = "
402
            "input/inlet, negative = output/outlet ".format(nEqUsed - 1)
403
        )
404

405
        f += head + "\n"
1✔
406
        f += tot + "\n"
1✔
407
        f += "\n"
1✔
408

409
        return f
1✔
410

411
    def exportPipeAndTeeTypesForTemp(self, startingUnit):
1✔
412
        # Prints the part of the export where the pipes, tp and div Units are printed
413

414
        f = ""
1✔
415
        unitNumber = startingUnit
1✔
416
        # typeNr1 = 929 # Temperature calculation from a tee-piece
417
        # typeNr2 = 931 # Temperature calculation from a pipe
418

419
        for t in self._hasInternalPipings:
1✔
420
            res = t.exportPipeAndTeeTypesForTemp(unitNumber)
1✔
421
            f += res[0]
1✔
422
            unitNumber = res[1]
1✔
423

424
        self._editor.printerUnitnr = unitNumber
1✔
425

426
        return f
1✔
427

428
    def exportSinglePipeEnergyBalanceVariables(self):
1✔
429
        simulatedSinglePipes = [
1✔
430
            ip
431
            for ip in self._hasInternalPipings
432
            if isinstance(ip, _spc.SinglePipeConnection)
433
            and ip.shallBeSimulated
434
        ]
435

436
        if not simulatedSinglePipes:
1✔
437
            return ""
1✔
438

439
        convectedFluxes = [
1✔
440
            sp.getConvectedHeatFluxVariableName()
441
            for sp in simulatedSinglePipes
442
        ]
443
        summedConvectedFluxes = "+".join(convectedFluxes)
1✔
444

445
        dissipatedFluxes = [
1✔
446
            sp.getDissipatedHeatFluxVariableName()
447
            for sp in simulatedSinglePipes
448
        ]
449
        summedDissipatedFluxes = "+".join(dissipatedFluxes)
1✔
450

451
        internalEnergies = [
1✔
452
            sp.getInternalHeatVariableName() for sp in simulatedSinglePipes
453
        ]
454
        summedInternalEnergies = "+".join(internalEnergies)
1✔
455

456
        Totals = _cnames.EnergyBalanceTotals.SinglePipe
1✔
457
        equation = f"""\
1✔
458
*** Single pipe losses
459
EQUATIONS 1
460
spPipeEnIntTot = {summedInternalEnergies}
461

462
UNIT 100 TYPE 993
463
PARAMETERS 1
464
1 ! Number of inputs
465
INPUTS 1
466
spPipeEnIntTot
467
**
468
0
469

470
EQUATIONS 4
471
{Totals.CONVECTED} = {summedConvectedFluxes}
472
{Totals.DISSIPATED} = {summedDissipatedFluxes}
473
{Totals.PIPE_INTERNAL_CHANGE} = (spPipeEnIntTot - [100,1]) / dtSim / 3600 ! kW
474
{Totals.IMBALANCE} = {Totals.CONVECTED} - {Totals.DISSIPATED} - {Totals.PIPE_INTERNAL_CHANGE}
475
"""
476
        return equation
1✔
477

478
    def exportDoublePipeEnergyBalanceVariables(self):
1✔
479

480
        doublePipes = [
1✔
481
            _gdpebe.DoublePipe(
482
                ip.displayName,
483
                ip.coldModelPipe.name,
484
                ip.hotModelPipe.name,
485
                ip.shallBeSimulated,
486
            )
487
            for ip in self._hasInternalPipings
488
            if isinstance(ip, _dpc.DoublePipeConnection)
489
        ]
490

491
        equations = _gdpebe.getDoublePipeEnergyBalanceEquations(doublePipes)
1✔
492

493
        return equations
1✔
494

495
    def exportMassFlowPrinter(self, unitnr, descLen):
1✔
496
        typenr = 25
1✔
497
        printingMode = 0
1✔
498
        relAbsStart = 0
1✔
499
        overwriteApp = -1
1✔
500
        printHeader = -1
1✔
501
        delimiter = 0
1✔
502
        printLabels = 1
1✔
503

504
        f = (
1✔
505
            "ASSIGN "
506
            + self._diagramName.rsplit(".", 1)[0]
507
            + "_Mfr.prt "
508
            + str(unitnr)
509
            + "\n\n"
510
        )
511

512
        f += "UNIT " + str(unitnr) + " TYPE " + str(typenr)
1✔
513
        f += " " * (descLen - len(f)) + "! User defined Printer" + "\n"
1✔
514
        f += "PARAMETERS 10" + "\n"
1✔
515
        f += "dtSim"
1✔
516
        f += " " * (descLen - len(f)) + "! 1 Printing interval" + "\n"
1✔
517
        f += "START"
1✔
518
        f += " " * (descLen - len(f)) + "! 2 Start time" + "\n"
1✔
519
        f += "STOP"
1✔
520
        f += " " * (descLen - len(f)) + "! 3 Stop time" + "\n"
1✔
521
        f += str(unitnr)
1✔
522
        f += " " * (descLen - len(f)) + "! 4 Logical unit" + "\n"
1✔
523
        f += str(printingMode)
1✔
524
        f += " " * (descLen - len(f)) + "! 5 Units printing mode" + "\n"
1✔
525
        f += str(relAbsStart)
1✔
526
        f += (
1✔
527
            " " * (descLen - len(f))
528
            + "! 6 Relative or absolute start time"
529
            + "\n"
530
        )
531
        f += str(overwriteApp)
1✔
532
        f += " " * (descLen - len(f)) + "! 7 Overwrite or Append" + "\n"
1✔
533
        f += str(printHeader)
1✔
534
        f += " " * (descLen - len(f)) + "! 8 Print header" + "\n"
1✔
535
        f += str(delimiter)
1✔
536
        f += " " * (descLen - len(f)) + "! 9 Delimiter" + "\n"
1✔
537
        f += str(printLabels)
1✔
538
        f += " " * (descLen - len(f)) + "! 10 Print labels" + "\n"
1✔
539
        f += "\n"
1✔
540

541
        allVariableNames = []
1✔
542
        for hasInternalPiping in self._hasInternalPipings:
1✔
543
            if isinstance(hasInternalPiping, _spc.SinglePipeConnection):
1✔
544
                mfrVariableName = _mnames.getCanonicalMassFlowVariableName(
1✔
545
                    componentDisplayName=hasInternalPiping.getDisplayName(),
546
                    pipeName=hasInternalPiping.modelPipe.name,
547
                )
548
                allVariableNames.append(mfrVariableName)
1✔
549
            elif isinstance(hasInternalPiping, _dpc.DoublePipeConnection):
1✔
550
                coldMfrVariableName = _mnames.getCanonicalMassFlowVariableName(
1✔
551
                    componentDisplayName=hasInternalPiping.getDisplayName(),
552
                    pipeName=hasInternalPiping.coldModelPipe.name,
553
                )
554
                hotMfrVariableName = _mnames.getCanonicalMassFlowVariableName(
1✔
555
                    componentDisplayName=hasInternalPiping.getDisplayName(),
556
                    pipeName=hasInternalPiping.hotModelPipe.name,
557
                )
558
                allVariableNames.extend(
1✔
559
                    [coldMfrVariableName, hotMfrVariableName]
560
                )
561
            elif isinstance(hasInternalPiping, _tventil.TVentil):
1✔
562
                inputVariableName = _mnames.getInputVariableName(
1✔
563
                    hasInternalPiping, hasInternalPiping.modelDiverter
564
                )
565
                allVariableNames.append(inputVariableName)
1✔
566

567
        variableNameOctets = [
1✔
568
            allVariableNames[s : s + 8]
569
            for s in range(0, len(allVariableNames), 8)
570
        ]
571

572
        formattedInputVariables = (
1✔
573
            "\n".join(" ".join(o) for o in variableNameOctets) + "\n"
574
        )
575

576
        f += f"""\
1✔
577
INPUTS {len(allVariableNames)}
578
{formattedInputVariables}
579
***
580
{formattedInputVariables}
581

582
"""
583
        return f
1✔
584

585
    def exportTempPrinter(self, unitnr, descLen):
1✔
586

587
        typenr = 25
1✔
588
        printingMode = 0
1✔
589
        relAbsStart = 0
1✔
590
        overwriteApp = -1
1✔
591
        printHeader = -1
1✔
592
        delimiter = 0
1✔
593
        printLabels = 1
1✔
594

595
        f = (
1✔
596
            "ASSIGN "
597
            + self._diagramName.rsplit(".", 1)[0]
598
            + "_T.prt "
599
            + str(unitnr)
600
            + "\n\n"
601
        )
602

603
        f += "UNIT " + str(unitnr) + " TYPE " + str(typenr)
1✔
604
        f += " " * (descLen - len(f)) + "! User defined Printer" + "\n"
1✔
605
        f += "PARAMETERS 10" + "\n"
1✔
606
        f += "dtSim"
1✔
607
        f += " " * (descLen - len(f)) + "! 1 Printing interval" + "\n"
1✔
608
        f += "START"
1✔
609
        f += " " * (descLen - len(f)) + "! 2 Start time" + "\n"
1✔
610
        f += "STOP"
1✔
611
        f += " " * (descLen - len(f)) + "! 3 Stop time" + "\n"
1✔
612
        f += str(unitnr)
1✔
613
        f += " " * (descLen - len(f)) + "! 4 Logical unit" + "\n"
1✔
614
        f += str(printingMode)
1✔
615
        f += " " * (descLen - len(f)) + "! 5 Units printing mode" + "\n"
1✔
616
        f += str(relAbsStart)
1✔
617
        f += (
1✔
618
            " " * (descLen - len(f))
619
            + "! 6 Relative or absolute start time"
620
            + "\n"
621
        )
622
        f += str(overwriteApp)
1✔
623
        f += " " * (descLen - len(f)) + "! 7 Overwrite or Append" + "\n"
1✔
624
        f += str(printHeader)
1✔
625
        f += " " * (descLen - len(f)) + "! 8 Print header" + "\n"
1✔
626
        f += str(delimiter)
1✔
627
        f += " " * (descLen - len(f)) + "! 9 Delimiter" + "\n"
1✔
628
        f += str(printLabels)
1✔
629
        f += " " * (descLen - len(f)) + "! 10 Print labels" + "\n"
1✔
630
        f += "\n"
1✔
631

632
        allVariableNames = []
1✔
633
        for hasInternalPiping in self._hasInternalPipings:
1✔
634
            if not isinstance(
1✔
635
                hasInternalPiping,
636
                (_spc.SinglePipeConnection, _dpc.DoublePipeConnection),
637
            ):
638
                continue
1✔
639

640
            internalPiping = hasInternalPiping.getInternalPiping()
1✔
641
            nodes = internalPiping.nodes
1✔
642

643
            temperatureVariableNames = self._getOutputTemperatureVariableNames(
1✔
644
                hasInternalPiping, nodes
645
            )
646

647
            allVariableNames.extend(temperatureVariableNames)
1✔
648

649
        variableNameOctets = [
1✔
650
            allVariableNames[s : s + 8]
651
            for s in range(0, len(allVariableNames), 8)
652
        ]
653

654
        formattedInputVariables = (
1✔
655
            "\n".join(" ".join(s) for s in variableNameOctets) + "\n"
656
        )
657

658
        f += f"""\
1✔
659
INPUTS {len(allVariableNames)}
660
{formattedInputVariables}
661
***
662
{formattedInputVariables}
663

664
"""
665
        return f
1✔
666

667
    @staticmethod
1✔
668
    def _getOutputTemperatureVariableNames(hasInternalPiping, nodes):
1✔
669
        temperatureVariableNames = []
1✔
670
        for n in nodes:
1✔
671
            temperatureVariableName = _temps.getTemperatureVariableName(
1✔
672
                hasInternalPiping.shallRenameOutputTemperaturesInHydraulicFile(),
673
                componentDisplayName=hasInternalPiping.getDisplayName(),
674
                nodeName=n.name,
675
            )
676
            temperatureVariableNames.append(temperatureVariableName)
1✔
677
        return temperatureVariableNames
1✔
678

679
    def exportFluids(self) -> str:
1✔
680
        def getValueOrVariableName(
1✔
681
            valueOrVariable: float | _hlm.Variable, factor: float = 1
682
        ) -> float | str:
683
            if isinstance(valueOrVariable, float):
1✔
684
                return valueOrVariable * factor
×
685

686
            if isinstance(valueOrVariable, _hlm.Variable):
1✔
687
                if factor == 1:
1✔
688
                    return valueOrVariable.name
1✔
689

690
                return f"{valueOrVariable.name}*{factor}"
1✔
691

NEW
692
            raise ValueError(
×
693
                f"Can't deal with type {type(valueOrVariable).__name__}."
694
            )
695

696
        template = """\
1✔
697
** Fluids:
698
EQUATIONS {{2 * fluids|length}}
699
{% for fluid in fluids -%}
700
** {{fluid.name}}
701
{% set rho = getValueOrVariableName(fluid.densityInKgPerM3) -%}
702
{% set cp = getValueOrVariableName(fluid.specificHeatCapacityInJPerKgK, 1e-3) -%}
703
F{{fluid.name}}Rho = {{rho}} ! [kg/m^3]
704
F{{fluid.name}}Cp = {{cp}} ! [kJ/(kg*K)]
705
{% endfor -%}
706
"""
707
        fluids = self._fluids
1✔
708
        return self._render(
1✔
709
            template,
710
            fluids=fluids,
711
            getValueOrVariableName=getValueOrVariableName,
712
        )
713

714
    def exportHydraulicLoops(self) -> str:
1✔
715
        template = """\
1✔
716
** Hydraulic loops
717
EQUATIONS {{nEquations}}
718
{% for hydraulicLoop in loops -%}
719
{% set loopName = hydraulicLoop.name.value -%}
720
{% set loopRho = names.getDensityName(loopName) -%}
721
{% set loopCp = names.getHeatCapacityName(loopName) -%}
722
{% set fluid = hydraulicLoop.fluid -%}
723
{% set loopLen = names.getDefaultLengthName(loopName) -%}
724
{% set loopDia = names.getDefaultDiameterName(loopName) -%}
725
{% set loopUVal = names.getDefaultUValueName(loopName) -%}
726
{% set loopNPipes = names.getNumberOfPipesName(loopName) -%}
727
** {{loopName}}
728
{% if hydraulicLoop.connectionsDefinitionMode.useLoopWideDefaults() -%}
729
{{loopNPipes}} = {{hydraulicLoop.connections | length}}
730
{{loopLen}} = {{values.DEFAULT_LENGTH_IN_M}} ! [m]
731
{{loopDia}} = {{values.DEFAULT_DIAMETER_IN_CM / 100}} ! [m]
732
{{loopUVal}} = {{values.DEFAULT_U_VALUE_IN_W_PER_M2_K * 60*60/1000}} ! [kJ/(h*m^2*K)] (= {{values.DEFAULT_U_VALUE_IN_W_PER_M2_K}} W/(m^2*K))
733
{% endif -%}
734
{{loopRho}} = F{{fluid.name}}Rho
735
{{loopCp}} = F{{fluid.name}}Cp
736

737
{% endfor -%}
738
"""
739
        loops = self._hydraulicLoops
1✔
740

741
        nEquations = sum(
1✔
742
            6 if l.connectionsDefinitionMode.useLoopWideDefaults() else 2
743
            for l in loops
744
        )
745

746
        return self._render(
1✔
747
            template,
748
            loops=loops,
749
            nEquations=nEquations,
750
            names=_lnames,
751
            values=_spdValues,
752
        )
753

754
    @staticmethod
1✔
755
    def _render(template: str, /, **kwargs):
1✔
756
        compiledTemplate = _jinja.Template(template)
1✔
757
        return compiledTemplate.render(**kwargs)
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc