1# -*- coding: utf-8 -*-
2
3#-------------------------------------------------------------------------------
4
5# This file is part of Code_Saturne, a general-purpose CFD tool.
6#
7# Copyright (C) 1998-2021 EDF S.A.
8#
9# This program is free software; you can redistribute it and/or modify it under
10# the terms of the GNU General Public License as published by the Free Software
11# Foundation; either version 2 of the License, or (at your option) any later
12# version.
13#
14# This program is distributed in the hope that it will be useful, but WITHOUT
15# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
16# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
17# details.
18#
19# You should have received a copy of the GNU General Public License along with
20# this program; if not, write to the Free Software Foundation, Inc., 51 Franklin
21# Street, Fifth Floor, Boston, MA 02110-1301, USA.
22
23#-------------------------------------------------------------------------------
24
25"""
26This module defines the lagrangian two phase flow modelling management.
27
28This module contains the following classes and function:
29- LagrangianBoundariesModel
30- LagrangianBoundariesTestCase
31"""
32
33
34#-------------------------------------------------------------------------------
35# Library modules import
36#-------------------------------------------------------------------------------
37
38
39import sys, unittest, logging
40
41
42#-------------------------------------------------------------------------------
43# Application modules import
44#-------------------------------------------------------------------------------
45
46
47from code_saturne.model.Common import *
48from code_saturne.model.XMLvariables import Model, Variables
49from code_saturne.model.LagrangianModel import LagrangianModel
50from code_saturne.model.CoalCombustionModel import CoalCombustionModel
51
52
53#-------------------------------------------------------------------------------
54# log config
55#-------------------------------------------------------------------------------
56
57
58logging.basicConfig()
59log = logging.getLogger("LagrangianBoundariesModel")
60log.setLevel(GuiParam.DEBUG)
61
62
63#-------------------------------------------------------------------------------
64# lagrangian model class
65#-------------------------------------------------------------------------------
66
67
68class LagrangianBoundariesModel(Model):
69    """
70    Manage the input/output markups in the xml doc about Lagrangian module.
71    """
72    def __init__(self, case):
73        """
74        Constructor.
75        """
76        self.case = case
77        self.node_boundaries = self.case.root().xmlInitNode('boundary_conditions')
78        self.default = self.defaultParticlesBoundaryValues()
79
80
81    def defaultParticlesBoundaryValues(self):
82        """
83        Return a dictionnary which contains default values.
84        """
85        default = {}
86        default['particles'] = "inlet"
87        default['n_is'] = 0
88        default['number'] = 10
89        default['frequency'] = 1
90        default['statistical_groups'] = 0.
91        default['statistical_weight_choice'] = "prescribed"
92        default['statistical_weight'] = 1.0
93        default['mass_flow_rate'] = 0.
94        default['density'] = 1000.
95        default['velocity_choice'] = "fluid"
96        default['velocity_norm'] = 0.
97        default['velocity_value'] = 0.
98        default['temperature_choice'] = "fluid"
99        default['temperature'] = 20.
100        default['specific_heat'] = 1400.
101        default['emissivity'] = 0.9
102        default['diameter'] = 1.0e-5
103        default['diameter_standard_deviation'] = 0.
104        default['fouling_index'] = 1.
105        default['coal_number'] = 1
106        default['coal_temperature'] = 800.
107        return default
108
109    def getFoulingStatus(self):
110        """
111        Return fouling status
112        """
113        return LagrangianModel(self.case).getCoalFouling()
114
115
116    @Variables.undoGlobal
117    def setBoundaryChoice(self, nature, labelbc, value):
118        """
119        Update value for the boundary condition. Here we defined the xml nodes
120        'self.node_boundary' and 'self.node_particles' used in many functions.
121        """
122        if nature == "inlet":
123            self.isInList(value, ["inlet", "bounce", "outlet"])
124        elif nature == "outlet":
125            self.isInList(value, ["outlet"])
126        elif nature == "free_inlet_outlet":
127            self.isInList(value, ["inlet", "outlet"])
128        elif nature == "imposed__outlet":
129            self.isInList(value, ["outlet"])
130        elif nature == "symmetry":
131            self.isInList(value, ["part_symmetry", "bounce"])
132        elif nature == "wall":
133            l = [ "inlet", "bounce", "deposit1", "deposit2"]
134            if LagrangianModel(self.case).getCoalFouling() == "on":
135                l.append("fouling")
136            self.isInList(value, l)
137        self.node_boundary = self.node_boundaries.xmlInitChildNode(nature, label=labelbc, field_id='none')
138        self.node_particles = self.node_boundary.xmlInitChildNode('particles', 'choice')
139        self.node_particles['choice'] = value
140        self.setCurrentBoundaryNode(nature, labelbc)
141
142
143    @Variables.noUndo
144    def getBoundaryChoice(self, nature, labelbc):
145        """
146        Return value for the boundary condition.
147        """
148        default = { "wall" : "deposit1", "inlet" : "inlet",
149                    "outlet" : "outlet", "free_inlet_outlet" : "outlet",
150                    "imposed_p_outlet" : "outlet",
151                    "symmetry" : "part_symmetry"}
152        self.setCurrentBoundaryNode(nature, labelbc)
153        if self.node_particles:
154            val = self.node_particles['choice']
155            if val == None or val == "":
156                val = default[nature]
157                self.setBoundaryChoice(nature, labelbc, val)
158        return val
159
160
161    @Variables.undoLocal
162    def setCurrentBoundaryNode(self, nature, labelbc):
163        """
164        Update the current boundary node.
165        """
166        self.node_boundary = self.node_boundaries.xmlInitChildNode(nature, label=labelbc, field_id='none')
167        self.node_particles = self.node_boundary.xmlInitChildNode('particles', 'choice')
168
169
170    def newSetNode(self):
171        """
172        Add a new 'set' node with child nodes.
173        """
174        node_set = self.node_particles.xmlAddChild('class')
175        node_set.xmlSetData('number', self.default['number'])
176        node_set.xmlSetData('frequency', self.default['frequency'])
177        node_set.xmlSetData('statistical_groups', self.default['statistical_groups'])
178        node_set.xmlSetData('mass_flow_rate', self.default['mass_flow_rate'])
179        if CoalCombustionModel(self.case).getCoalCombustionModel("only") == 'off':
180            node_set.xmlSetData('density', self.default['density'])
181            node_set.xmlInitChildNode('temperature',
182                                      choice=self.default['temperature_choice'])
183            node_set.xmlSetData('temperature',
184                                self.default['temperature'])
185
186        node_set.xmlInitChildNode('statistical_weight',
187                                  choice=self.default['statistical_weight_choice'])
188        node_set.xmlSetData('statistical_weight',
189                            self.default['statistical_weight'])
190
191        node_set.xmlInitChildNode('velocity',
192                                  choice=self.default['velocity_choice'])
193
194        node_set.xmlInitChildNode('diameter')
195        node_set.xmlSetData('diameter', self.default['diameter'])
196        node_set.xmlSetData('diameter_standard_deviation',
197                            self.default['diameter_standard_deviation'])
198        node_set.xmlSetData('fouling_index', self.default['fouling_index'])
199
200
201    @Variables.undoGlobal
202    def setNumberOfSetsValue(self, value):
203        """
204        Update the number of sets. Create or delete nodes if necessary.
205        """
206        self.isInt(value)
207        self.isGreaterOrEqual(value, 0)
208        node_list = self.node_particles.xmlGetChildNodeList('class')
209        nnodes = len(node_list)
210        if value > nnodes:
211            for i in range(value-nnodes):
212                self.newSetNode()
213        else:
214            for i in range(nnodes - value):
215                node_to_delete = node_list.pop()
216                node_to_delete.xmlRemoveNode()
217            # redefine self.node_set
218
219
220    @Variables.noUndo
221    def getNumberOfSetsValue(self):
222        """
223        Return the number of injection sets.
224        """
225        node_list = self.node_particles.xmlGetChildNodeList('class')
226        value = len(node_list)
227        if value == None:
228            value = self.defaultParticlesBoundaryValues()['n_is']
229            self.setNumberOfSetsValue(value)
230        return value
231
232
233    @Variables.undoLocal
234    def setCurrentSetNode(self, iset):
235        """
236        Update the current set node.
237        """
238        choice = self.node_particles['choice']
239        self.isInList(choice, ["inlet"])
240        self.isInt(iset)
241        self.node_set = None
242        nodes_list = self.node_particles.xmlGetChildNodeList('class')
243        if nodes_list:
244            nnodes = len(nodes_list)
245            self.isLowerOrEqual(iset, nnodes)
246            self.node_set = nodes_list[iset-1]
247
248
249    @Variables.undoLocal
250    def setNumberOfParticulesInSetValue(self, label, iset, value):
251        """
252        Update the number of particles in a set.
253        """
254        self.isInt(value)
255        self.isGreaterOrEqual(value, 0)
256        self.node_set.xmlSetData('number', value)
257
258
259    @Variables.noUndo
260    def getNumberOfParticulesInSetValue(self, label, iset):
261        """
262        Return the number of particles in a set.
263        """
264        value = self.node_set.xmlGetInt('number')
265        if value == None:
266            value = self.defaultParticlesBoundaryValues()['number']
267            self.setNumberOfParticulesInZoneValue(label, iset,value)
268        return value
269
270
271    @Variables.undoLocal
272    def setInjectionFrequencyValue(self, label, iset, value):
273        """
274        Update the injection frequency.
275        """
276        self.isInt(value)
277        self.isGreaterOrEqual(value, 0)
278        self.node_set.xmlSetData('frequency', value)
279
280
281    @Variables.noUndo
282    def getInjectionFrequencyValue(self, label, iset):
283        """
284        Return the injection frequency.
285        """
286        value = self.node_set.xmlGetInt('frequency')
287        if value == None:
288            value = self.defaultParticlesBoundaryValues()['frequency']
289            self.setInjectionFrequencyValue(label, iset, value)
290        return value
291
292
293    @Variables.undoLocal
294    def setParticleGroupNumberValue(self, label, iset, value):
295        """
296        Update the group number of the particle.
297        """
298        self.isInt(value)
299        self.isGreaterOrEqual(value, 0)
300        self.node_set.xmlSetData('statistical_groups', value)
301
302
303    @Variables.noUndo
304    def getParticleGroupNumberValue(self, label, iset):
305        """
306        Return the group number of the particle.
307        """
308        value = self.node_set.xmlGetInt('statistical_groups')
309        if value == None:
310            value = self.defaultParticlesBoundaryValues()['statistical_groups']
311            self.setParticleGroupNumberValue(label, iset, value)
312        return value
313
314
315    @Variables.undoLocal
316    def setMassFlowRateValue(self, label, iset, value):
317        """
318        Update the mass flow rate value.
319        """
320        self.isFloat(value)
321        self.isGreaterOrEqual(value, 0)
322        self.node_set.xmlSetData('mass_flow_rate', value)
323
324
325    @Variables.noUndo
326    def getMassFlowRateValue(self, label, iset):
327        """
328        Return the mass flow rate value.
329        """
330        value = self.node_set.xmlGetDouble('mass_flow_rate')
331        if value == None:
332            value = self.defaultParticlesBoundaryValues()['mass_flow_rate']
333            self.setMassFlowRateValue(label, iset, value)
334        return value
335
336
337    @Variables.undoLocal
338    def setStatisticalWeightChoice(self, label, iset, value):
339        """
340        Update the condition on statistical weight.
341        """
342        self.isInList(value, ["rate", "prescribed"])
343        node = self.node_set.xmlInitChildNode('statistical_weight', 'choice')
344        node['choice'] = value
345
346
347    @Variables.noUndo
348    def getStatisticalWeightChoice(self, label, iset):
349        """
350        Return the condition on statistical weight.
351        """
352        val = None
353        node = self.node_set.xmlGetChildNode('statistical_weight', 'choice')
354        if node:
355            val = node['choice']
356        if val == None or val == "":
357            val = self.defaultParticlesBoundaryValues()['statistical_weight_choice']
358        return val
359
360
361    @Variables.undoLocal
362    def setStatisticalWeightValue(self, label, iset, value):
363        """
364        Update the statistical weight value.
365        """
366        self.isFloat(value)
367        self.isGreater(value, 0)
368        self.node_set.xmlSetData('statistical_weight', value)
369
370
371    @Variables.noUndo
372    def getStatisticalWeightValue(self, label, iset):
373        """
374        Return the statistical weight value.
375        """
376        value = self.node_set.xmlGetDouble('statistical_weight')
377        if value == None:
378            value = self.defaultParticlesBoundaryValues()['statistical_weight']
379            self.setStatisticalWeightValue(label, iset, value)
380        return value
381
382
383    @Variables.undoLocal
384    def setDensityValue(self, label, iset, value):
385        """
386        Update the density value.
387        """
388        self.isFloat(value)
389        self.isGreaterOrEqual(value, 0)
390        self.node_set.xmlSetData('density', value)
391
392
393    @Variables.noUndo
394    def getDensityValue(self, label, iset):
395        """
396        Return the density value.
397        """
398        value = self.node_set.xmlGetDouble('density')
399        if value == None:
400            value = self.defaultParticlesBoundaryValues()['density']
401            self.setDensityValue(label, iset, value)
402        return value
403
404    @Variables.undoLocal
405    def setFoulingIndexValue(self, label, iset, value):
406        """
407        Update the fouling index value.
408        """
409        self.isFloat(value)
410        self.isGreaterOrEqual(value, 0)
411        self.node_set.xmlSetData('fouling_index', value)
412
413
414    @Variables.noUndo
415    def getFoulingIndexValue(self, label, iset):
416        """
417        Return the fouling index value.
418        """
419        value = self.node_set.xmlGetDouble('fouling_index')
420        if value == None:
421            value = self.defaultParticlesBoundaryValues()['fouling_index']
422            self.setFoulingIndexValue(label, iset, value)
423        return value
424
425
426    @Variables.undoLocal
427    def setVelocityChoice(self, label, iset, choice):
428        """
429        Update the condition on velocity.
430        """
431        self.isInList(choice, ["fluid", "components", "norm"])
432        node_velocity = self.node_set.xmlInitChildNode('velocity', 'choice')
433        node_velocity['choice'] = choice
434        if choice in ["fluid", "norm"]:
435            node_velocity.xmlRemoveChild('velocity_x')
436            node_velocity.xmlRemoveChild('velocity_y')
437            node_velocity.xmlRemoveChild('velocity_z')
438        elif choice in ["fluid", "components"]:
439            node_velocity.xmlRemoveChild('norm')
440
441
442    @Variables.noUndo
443    def getVelocityChoice(self, label, iset):
444        """
445        Return the condition on velocity.
446        """
447        node = self.node_set.xmlInitChildNode('velocity', 'choice')
448        if node:
449            val = node['choice']
450            if val == None:
451                val = self.defaultParticlesBoundaryValues()['velocity_choice']
452                self.setVelocityChoice(val)
453        return val
454
455
456    @Variables.undoLocal
457    def setVelocityNormValue(self, label, iset, value):
458        """
459        Update the velocity norm.
460        """
461        self.isFloat(value)
462        self.isGreaterOrEqual(value, 0.)
463        node_velocity = self.node_set.xmlInitChildNode('velocity', choice="norm")
464        choice = node_velocity['choice']
465        self.isInList(choice, ["norm"])
466        node_velocity.xmlSetData('norm', value)
467
468
469    @Variables.noUndo
470    def getVelocityNormValue(self, label, iset):
471        """
472        Return the velocity norm.
473        """
474        node_velocity = self.node_set.xmlInitChildNode('velocity', choice="norm")
475        value = node_velocity.xmlGetDouble('norm')
476        if value == None:
477            value = self.defaultParticlesBoundaryValues()['velocity_norm']
478            self.setVelocityNormValue(label, iset, value)
479        return value
480
481
482    @Variables.undoLocal
483    def setVelocityDirectionValue(self, label, iset, idir, value):
484        """
485        Update the velocity value in the given direction.
486        """
487        self.isFloat(value)
488        self.isGreaterOrEqual(value, 0.)
489        node_velocity = self.node_set.xmlInitChildNode('velocity', choice="components")
490        choice = node_velocity['choice']
491        self.isInList(choice, ["components"])
492        node_velocity.xmlSetData('velocity_' + idir, value)
493
494
495    @Variables.noUndo
496    def getVelocityDirectionValue(self, label, iset, idir):
497        """
498        Return the velocity value in the given direction.
499        """
500        node_velocity = self.node_set.xmlInitChildNode('velocity', choice="components")
501        value = self.node_set.xmlGetDouble('velocity_' + idir)
502        if value == None:
503            value = self.defaultParticlesBoundaryValues()['velocity_value']
504            self.setVelocityDirectionValue(label, iset, idir, value)
505        return value
506
507
508    @Variables.undoLocal
509    def setTemperatureChoice(self, label, iset, value):
510        """
511        Update the condition on temperature.
512        """
513        self.isInList(value, ["prescribed", "fluid"])
514        node = self.node_set.xmlInitChildNode('temperature', 'choice')
515        node['choice'] = value
516
517
518    @Variables.noUndo
519    def getTemperatureChoice(self, label, iset):
520        """
521        Return the condition on temperature.
522        """
523        val = None
524        node = self.node_set.xmlGetChildNode('temperature', 'choice')
525        if node:
526            val = node['choice']
527        if val == None:
528            val = self.defaultParticlesBoundaryValues()['temperature_choice']
529        return val
530
531
532    @Variables.undoLocal
533    def setTemperatureValue(self, label, iset, value):
534        """
535        Update the temperature value.
536        """
537        self.isFloat(value)
538        self.isGreaterOrEqual(value, 0)
539        self.node_set.xmlSetData('temperature', value)
540
541
542    @Variables.noUndo
543    def getTemperatureValue(self, label, iset):
544        """
545        Return the temperature value.
546        """
547        value = self.node_set.xmlGetDouble('temperature')
548        if value == None:
549            value = self.defaultParticlesBoundaryValues()['temperature']
550            self.setTemperatureValue(label, iset, value)
551        return value
552
553
554    @Variables.undoLocal
555    def setSpecificHeatValue(self, label, iset, value):
556        """
557        Update the specific heat value.
558        """
559        self.isFloat(value)
560        self.isGreaterOrEqual(value, 0)
561        self.node_set.xmlSetData('specific_heat', value)
562
563
564    @Variables.noUndo
565    def getSpecificHeatValue(self, label, iset):
566        """
567        Return the specific heat value.
568        """
569        value = self.node_set.xmlGetDouble('specific_heat')
570        if value == None:
571            value = self.defaultParticlesBoundaryValues()['specific_heat']
572            self.setSpecificHeatValue(label, iset, value)
573        return value
574
575
576    @Variables.undoLocal
577    def setEmissivityValue(self, label, iset, value):
578        """
579        Update the emissivity value.
580        """
581        self.isFloat(value)
582        self.isGreaterOrEqual(value, 0)
583        self.node_set.xmlSetData('emissivity', value)
584
585
586    @Variables.noUndo
587    def getEmissivityValue(self, label, iset):
588        """
589        Return the emissivity value.
590        """
591        value = self.node_set.xmlGetDouble('emissivity')
592        if value == None:
593            value = self.defaultParticlesBoundaryValues()['emissivity']
594            self.setEmissivityValue(label, iset, value)
595        return value
596
597
598    @Variables.undoLocal
599    def setDiameterValue(self, label, iset, value):
600        """
601        Update the particle diameter value.
602        """
603        self.isFloat(value)
604        self.isGreaterOrEqual(value, 0)
605        self.node_set.xmlSetData('diameter', value)
606
607
608    @Variables.noUndo
609    def getDiameterValue(self, label, iset):
610        """
611        Return the particle diameter value.
612        """
613        value = self.node_set.xmlGetDouble('diameter')
614        if value == None:
615            value = self.defaultParticlesBoundaryValues()['diameter']
616            self.setDiameterValue(label, iset, value)
617        return value
618
619
620    @Variables.undoLocal
621    def setDiameterVarianceValue(self, label, iset, value):
622        """
623        Update the particle diameter variance value.
624        """
625        self.isFloat(value)
626        self.isGreaterOrEqual(value, 0)
627        self.node_set.xmlSetData('diameter_standard_deviation', value)
628
629
630    @Variables.noUndo
631    def getDiameterVarianceValue(self, label, iset):
632        """
633        Return the particle diameter variance value.
634        """
635        value = self.node_set.xmlGetDouble('diameter_standard_deviation')
636        if value == None:
637            value = self.defaultParticlesBoundaryValues()['diameter_standard_deviation']
638            self.setDiameterVarianceValue(label, iset, value)
639        return value
640
641
642    @Variables.undoLocal
643    def setCoalNumberValue(self, label, iset, value):
644        """
645        Update the coal number of the particle.
646        """
647        self.isInt(value)
648        self.isGreaterOrEqual(value, 0)
649        self.node_set.xmlSetData('coal_number', value)
650
651
652    @Variables.noUndo
653    def getCoalNumberValue(self, label, iset):
654        """
655        Return the coal number of the particle.
656        """
657        value = self.node_set.xmlGetInt('coal_number')
658        if value == None:
659            value = self.defaultParticlesBoundaryValues()['coal_number']
660            self.setCoalNumberValue(label, iset, value)
661        return value
662
663
664    @Variables.undoLocal
665    def setCoalTemperatureValue(self, label, iset, value):
666        """
667        Update the coal temperature.
668        """
669        self.isFloat(value)
670        self.isGreaterOrEqual(value, 0)
671        self.node_set.xmlSetData('coal_temperature', value)
672
673
674    @Variables.noUndo
675    def getCoalTemperatureValue(self, label, iset):
676        """
677        Return the coal temperature.
678        """
679        value = self.node_set.xmlGetDouble('coal_temperature')
680        if value == None:
681            value = self.defaultParticlesBoundaryValues()['coal_temperature']
682            self.setCoalTemperatureValue(label, iset, value)
683        return value
684
685
686#-------------------------------------------------------------------------------
687# LagrangianBoundaries test case
688#-------------------------------------------------------------------------------
689
690
691class LagrangianBoundariesTestCase(unittest.TestCase):
692    """
693    """
694    def setUp(self):
695        """
696        This method is executed before all "check" methods.
697        """
698        from code_saturne.model.XMLengine import Case
699        from code_saturne.model.XMLinitialize import XMLinit
700        self.case = Case()
701        XMLinit(self.case).initialize()
702
703
704    def tearDown(self):
705        """
706        This method is executed after all "check" methods.
707        """
708        del self.case
709
710
711    def checkLagrangianBoundariesInstantiation(self):
712        """
713        Check whether the LagrangianBoundariesModel class could be instantiated
714        """
715        model = None
716        model = LagrangianBoundariesModel(self.case)
717
718        assert model != None, 'Could not instantiate LagrangianBoundariesModel'
719
720
721    def checkLagrangianBoundariesDefaultValues(self):
722        """
723        Check the default values
724        """
725        model = LagrangianBoundariesModel(self.case)
726        doc = """"""
727
728        assert model.node_output == self.xmlNodeFromString(doc),\
729               'Could not get default values for model'
730
731
732def suite():
733    testSuite = unittest.makeSuite(LagrangianBoundariesTestCase, "check")
734    return testSuite
735
736
737def runTest():
738    print("LagrangianBoundariesTestCase TODO*********.")
739    runner = unittest.TextTestRunner()
740    runner.run(suite())
741
742
743#-------------------------------------------------------------------------------
744# End
745#-------------------------------------------------------------------------------
746