comfort.C
Go to the documentation of this file.
1 /*---------------------------------------------------------------------------*\
2  ========= |
3  \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
4  \\ / O peration | Website: https://openfoam.org
5  \\ / A nd | Copyright (C) 2019 OpenFOAM Foundation
6  \\/ M anipulation |
7 -------------------------------------------------------------------------------
8 License
9  This file is part of OpenFOAM.
10 
11  OpenFOAM is free software: you can redistribute it and/or modify it
12  under the terms of the GNU General Public License as published by
13  the Free Software Foundation, either version 3 of the License, or
14  (at your option) any later version.
15 
16  OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
17  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
18  FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
19  for more details.
20 
21  You should have received a copy of the GNU General Public License
22  along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
23 
24 \*---------------------------------------------------------------------------*/
25 
26 #include "comfort.H"
27 #include "wallFvPatch.H"
29 
30 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
31 
32 namespace Foam
33 {
34 namespace functionObjects
35 {
36  defineTypeNameAndDebug(comfort, 0);
37  addToRunTimeSelectionTable(functionObject, comfort, dictionary);
38 }
39 }
40 
41 
42 // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
43 
44 Foam::tmp<Foam::volScalarField> Foam::functionObjects::comfort::magU() const
45 {
46  tmp<volScalarField> tmagU = mag(lookupObject<volVectorField>("U"));
47 
48  // Switch to use the averaged velocity field in the domain.
49  // Consistent with EN ISO 7730 but does not make physical sense
50  if (meanVelocity_)
51  {
52  tmagU.ref() = tmagU->weightedAverage(mesh_.V());
53  }
54 
55  return tmagU;
56 }
57 
58 
59 Foam::dimensionedScalar Foam::functionObjects::comfort::Trad() const
60 {
61  dimensionedScalar Trad(Trad_);
62 
63  // The mean radiation is calculated by the mean wall temperatures
64  // which are summed and divided by the area | only walls are taken into
65  // account. This approach might be correct for a squared room but will
66  // defintely be inconsistent for complex room geometries. The norm does
67  // not provide any information about the calculation of this quantity.
68  if (!TradSet_)
69  {
70  const volScalarField::Boundary& TBf =
71  lookupObject<volScalarField>("T").boundaryField();
72 
73  scalar areaIntegral = 0;
74  scalar TareaIntegral = 0;
75 
76  forAll(TBf, patchi)
77  {
78  const fvPatchScalarField& pT = TBf[patchi];
79  const fvPatch& pTBf = TBf[patchi].patch();
80  const scalarField& pSf = pTBf.magSf();
81 
82  if (isType<wallFvPatch>(pTBf))
83  {
84  areaIntegral += gSum(pSf);
85  TareaIntegral += gSum(pSf*pT);
86  }
87  }
88 
89  Trad.value() = TareaIntegral/areaIntegral;
90  }
91 
92  // Bounds based on EN ISO 7730
93  if ((Trad.value() < 283.15) || (Trad.value() > 313.15))
94  {
96  << "The calculated mean wall radiation temperature is out of the\n"
97  << "bounds specified in EN ISO 7730:2006\n"
98  << "Valid range is 10 degC < T < 40 degC\n"
99  << "The actual value is: " << Trad - 273.15 << nl << endl;
100  }
101 
102  return Trad;
103 }
104 
105 
106 Foam::tmp<Foam::volScalarField> Foam::functionObjects::comfort::pSat() const
107 {
108  static const dimensionedScalar kPaToPa(dimPressure, 1000);
109  static const dimensionedScalar A(dimless, 16.6563);
110  static const dimensionedScalar B(dimTemperature, 4030.183);
111  static const dimensionedScalar C(dimTemperature, -38.15);
112 
113  tmp<volScalarField> tpSat(volScalarField::New("pSat", mesh_, pSat_));
114 
115  // Calculate the saturation pressure if no user input is given
116  if (pSat_.value() == 0)
117  {
118  const volScalarField& T = lookupObject<volScalarField>("T");
119 
120  // Equation based on ISO 7730:2006
121  tpSat = kPaToPa*exp(A - B/(T + C));
122  }
123 
124  return tpSat;
125 }
126 
127 
128 Foam::tmp<Foam::volScalarField> Foam::functionObjects::comfort::Tcloth
129 (
130  volScalarField& hc,
131  const dimensionedScalar& metabolicRateSI,
132  const dimensionedScalar& extWorkSI,
133  const volScalarField& T,
134  const dimensionedScalar& Trad
135 )
136 {
137  const dimensionedScalar factor1(dimTemperature, 308.85);
138 
139  const dimensionedScalar factor2
140  (
141  dimTemperature/metabolicRateSI.dimensions(),
142  0.028
143  );
144 
145  const dimensionedScalar factor3
146  (
147  dimensionSet(1, 0, -3, -4, 0, 0, 0),
148  3.96e-8
149  );
150 
151  // Heat transfer coefficient based on forced convection [W/m^2/K]
152  const volScalarField hcForced
153  (
154  dimensionedScalar(hc.dimensions()/sqrt(dimVelocity), 12.1)
155  *sqrt(magU())
156  );
157 
158  // Tcl [K] (surface cloth temperature)
159  tmp<volScalarField> tTcl
160  (
162  (
163  "Tcl",
164  T.mesh(),
166  )
167  );
168 
169  volScalarField& Tcl = tTcl.ref();
170 
171  // Initial guess
172  Tcl = T;
173 
174  label i = 0;
175 
176  Tcl.storePrevIter();
177 
178  // Iterative solving of equation (2)
179  do
180  {
181  Tcl = (Tcl + Tcl.prevIter())/2;
182  Tcl.storePrevIter();
183 
184  // Heat transfer coefficient based on natural convection
185  volScalarField hcNatural
186  (
187  dimensionedScalar(hc.dimensions()/pow025(dimTemperature), 2.38)
188  *pow025(mag(Tcl - T))
189  );
190 
191  // Set heat transfer coefficient based on equation (3)
192  hc =
193  pos(hcForced - hcNatural)*hcForced
194  + neg0(hcForced - hcNatural)*hcNatural;
195 
196  // Calculate surface temperature based on equation (2)
197  Tcl =
198  factor1
199  - factor2*(metabolicRateSI - extWorkSI)
200  - Icl_*factor3*fcl_*(pow4(Tcl) - pow4(Trad))
201  - Icl_*fcl_*hc*(Tcl - T);
202 
203  } while (!converged(Tcl) && i++ < maxClothIter_);
204 
205  if (i == maxClothIter_)
206  {
208  << "The surface cloth temperature did not converge within " << i
209  << " iterations\n";
210  }
211 
212  return tTcl;
213 }
214 
215 
216 bool Foam::functionObjects::comfort::converged
217 (
218  const volScalarField& phi
219 ) const
220 {
221  return
222  max(mag(phi.primitiveField() - phi.prevIter().primitiveField()))
223  < tolerance_;
224 }
225 
226 
227 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
228 
230 (
231  const word& name,
232  const Time& runTime,
233  const dictionary& dict
234 )
235 :
236  fvMeshFunctionObject(name, runTime, dict),
237  clothing_("clothing", dimless, 0),
238  metabolicRate_("metabolicRate", dimMass/pow3(dimTime), 0.8),
239  extWork_("extWork", dimMass/pow3(dimTime), 0),
240  TradSet_(false),
241  Trad_("Trad", dimTemperature, 0),
242  relHumidity_("relHumidity", dimless, 0.5),
243  pSat_("pSat", dimPressure, 0),
244  Icl_("Icl", dimensionSet(-1, 0, 3, 1, 0, 0, 0), 0),
245  fcl_("fcl", dimless, 0),
246  tolerance_(1e-4),
247  maxClothIter_(100),
248  meanVelocity_(false)
249 {
250  read(dict);
251 }
252 
253 
254 // * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
255 
257 {}
258 
259 
260 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
261 
263 {
264  clothing_.readIfPresent(dict);
265  metabolicRate_.readIfPresent(dict);
266  extWork_.readIfPresent(dict);
267  pSat_.readIfPresent(dict);
268  tolerance_ = dict.lookupOrDefault("tolerance", 1e-4);
269  maxClothIter_ = dict.lookupOrDefault("maxClothIter", 100);
270  meanVelocity_ = dict.lookupOrDefault<Switch>("meanVelocity", false);
271 
272  // Read relative humidity if provided and convert from % to fraction
273  if (dict.found(relHumidity_.name()))
274  {
275  relHumidity_.read(dict);
276  relHumidity_ /= 100;
277  }
278 
279  // Read radiation temperature if provided
280  if (dict.found(Trad_.name()))
281  {
282  TradSet_ = true;
283  Trad_.read(dict);
284  }
285  else
286  {
287  TradSet_ = false;
288  }
289 
290  Icl_ = dimensionedScalar(Icl_.dimensions(), 0.155)*clothing_;
291 
292  fcl_.value() =
293  Icl_.value() <= 0.078
294  ? 1.0 + 1.290*Icl_.value()
295  : 1.05 + 0.645*Icl_.value();
296 
297  return true;
298 }
299 
300 
302 {
303  const dimensionedScalar Trad(this->Trad());
304  const volScalarField pSat(this->pSat());
305 
306  const dimensionedScalar metabolicRateSI(58.15*metabolicRate_);
307  const dimensionedScalar extWorkSI(58.15*extWork_);
308 
309  const volScalarField& T = lookupObject<volScalarField>("T");
310 
311  // Heat transfer coefficient [W/m^2/K]
312  // This field is updated in Tcloth()
313  volScalarField hc
314  (
315  IOobject
316  (
317  "hc",
318  mesh_.time().timeName(),
319  mesh_
320  ),
321  mesh_,
322  dimensionedScalar(dimensionSet(1, 0, -3, -1, 0, 0, 0), 0)
323  );
324 
325  // Calculate the surface temperature of the cloth by an iterative
326  // process using equation (2) from DIN EN ISO 7730 [degC]
327  const volScalarField Tcloth
328  (
329  this->Tcloth
330  (
331  hc,
332  metabolicRateSI,
333  extWorkSI,
334  T,
335  Trad
336  )
337  );
338 
339  // Calculate the PMV quantity
340  const dimensionedScalar factor1(dimensionSet(-1, 0, 3, 0, 0, 0, 0), 0.303);
341  const dimensionedScalar factor2
342  (
343  dimless/metabolicRateSI.dimensions(),
344  -0.036
345  );
346  const dimensionedScalar factor3(factor1.dimensions(), 0.028);
347  const dimensionedScalar factor4(dimLength/dimTime, 3.05e-3);
348  const dimensionedScalar factor5(dimPressure, 5733);
349  const dimensionedScalar factor6(dimTime/dimLength, 6.99);
350  const dimensionedScalar factor8(metabolicRateSI.dimensions(), 58.15);
351  const dimensionedScalar factor9(dimless/dimPressure, 1.7e-5);
352  const dimensionedScalar factor10(dimPressure, 5867);
353  const dimensionedScalar factor11(dimless/dimTemperature, 0.0014);
354  const dimensionedScalar factor12(dimTemperature, 307.15);
355  const dimensionedScalar factor13
356  (
357  dimensionSet(1, 0, -3, -4, 0, 0, 0),
358  3.96e-8
359  );
360 
361  const scalar factor7
362  (
363  // Special treatment of Term4
364  // if metaRate - extWork < factor8, set to zero
365  (metabolicRateSI - extWorkSI).value() < factor8.value() ? 0 : 0.42
366  );
367 
368  Info<< "Calculating the predicted mean vote (PMV)\n";
369 
370  // Equation (1)
372  (
374  (
375  "PMV",
376 
377  // Term1: Thermal sensation transfer coefficient
378  (factor1*exp(factor2*metabolicRateSI) + factor3)
379  *(
380  (metabolicRateSI - extWorkSI)
381 
382  // Term2: Heat loss difference through skin
383  - factor4
384  *(
385  factor5
386  - factor6*(metabolicRateSI - extWorkSI)
387  - pSat*relHumidity_
388  )
389 
390  // Term3: Heat loss through sweating
391  - factor7*(metabolicRateSI - extWorkSI - factor8)
392 
393  // Term4: Heat loss through latent respiration
394  - factor9*metabolicRateSI*(factor10 - pSat*relHumidity_)
395 
396  // Term5: Heat loss through dry respiration
397  - factor11*metabolicRateSI*(factor12 - T)
398 
399  // Term6: Heat loss through radiation
400  - factor13*fcl_*(pow4(Tcloth) - pow4(Trad))
401 
402  // Term7: Heat loss through convection
403  - fcl_*hc*(Tcloth - T)
404  )
405  )
406  );
407 
408  Info<< "Calculating the predicted percentage of dissatisfaction (PPD)\n";
409 
410  // Equation (5) in EN ISO
412  (
414  (
415  "PPD",
416  100 - 95*exp(-0.03353*pow4(PMV()) - 0.21790*sqr(PMV()))
417  )
418  );
419 
420  return store(PMV) && store(PPD);
421 }
422 
423 
425 {
426  return writeObject("PMV") && writeObject("PPD");
427 }
428 
429 
430 // ************************************************************************* //
bool found(const word &, bool recursive=false, bool patternMatch=true) const
Search dictionary for given keyword.
Definition: dictionary.C:667
#define forAll(list, i)
Loop across all elements in list.
Definition: UList.H:434
intWM_LABEL_SIZE_t label
A label is an int32_t or int64_t as specified by the pre-processor macro WM_LABEL_SIZE.
Definition: label.H:59
A list of keyword definitions, which are a keyword followed by any number of values (e...
Definition: dictionary.H:158
dimensioned< Type > max(const dimensioned< Type > &, const dimensioned< Type > &)
dimensionedSymmTensor sqr(const dimensionedVector &dv)
addToRunTimeSelectionTable(functionObject, Qdot, dictionary)
static tmp< GeometricField< scalar, fvPatchField, volMesh > > New(const word &name, const Internal &, const PtrList< fvPatchField< scalar >> &)
Return a temporary field constructed from name,.
dimensionedScalar sqrt(const dimensionedScalar &ds)
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:251
dimensionedScalar pow025(const dimensionedScalar &ds)
A simple wrapper around bool so that it can be read as a word: true/false, on/off, yes/no, y/n, t/f, or none/any.
Definition: Switch.H:60
Generic dimensioned Type class.
Class to control time during OpenFOAM simulations that is also the top-level objectRegistry.
Definition: Time.H:68
Macros for easy insertion into run-time selection tables.
GeometricField< scalar, fvPatchField, volMesh > volScalarField
Definition: volFieldsFwd.H:57
dimensionedScalar pos(const dimensionedScalar &ds)
Dimension set for the base types.
Definition: dimensionSet.H:120
bool read(const char *, int32_t &)
Definition: int32IO.C:85
Type gSum(const FieldField< Field, Type > &f)
dimensionedScalar exp(const dimensionedScalar &ds)
fvPatchField< scalar > fvPatchScalarField
A class for handling words, derived from string.
Definition: word.H:59
Field< scalar > scalarField
Specialisation of Field<T> for scalar.
const dimensionSet dimTemperature(0, 0, 0, 1, 0, 0, 0)
Definition: dimensionSets.H:52
comfort(const word &name, const Time &runTime, const dictionary &dict)
Construct from Time and dictionary.
Definition: comfort.C:230
dimensionedScalar neg0(const dimensionedScalar &ds)
const Type & value() const
Return const reference to value.
virtual bool execute()
Calculate the predicted mean vote (PMV)
Definition: comfort.C:301
virtual bool read(const dictionary &)
Read the data needed for the comfort calculation.
Definition: comfort.C:262
const dimensionSet dimPressure
static const char nl
Definition: Ostream.H:260
void T(FieldField< Field, Type > &f1, const FieldField< Field, Type > &f2)
defineTypeNameAndDebug(Qdot, 0)
Internal & ref()
Return a reference to the dimensioned internal field.
dimensionedScalar pow3(const dimensionedScalar &ds)
label patchi
const dimensionSet dimless(0, 0, 0, 0, 0, 0, 0)
Definition: dimensionSets.H:47
T lookupOrDefault(const word &, const T &, bool recursive=false, bool patternMatch=true) const
Find and return a T,.
#define WarningInFunction
Report a warning using Foam::Warning.
const dimensionSet dimLength(0, 1, 0, 0, 0, 0, 0)
Definition: dimensionSets.H:50
dimensioned< scalar > dimensionedScalar
Dimensioned scalar obtained from generic dimensioned type.
virtual ~comfort()
Destructor.
Definition: comfort.C:256
dimensionedScalar pow4(const dimensionedScalar &ds)
const dimensionSet dimTime(0, 0, 1, 0, 0, 0, 0)
Definition: dimensionSets.H:51
const dimensionSet & dimensions() const
Return const reference to dimensions.
messageStream Info
dimensioned< scalar > mag(const dimensioned< Type > &)
const doubleScalar e
Elementary charge.
Definition: doubleScalar.H:105
Specialization of Foam::functionObject for an Foam::fvMesh, providing a reference to the Foam::fvMesh...
const dimensionSet dimMass(1, 0, 0, 0, 0, 0, 0)
Definition: dimensionSets.H:49
A class for managing temporary objects.
Definition: PtrList.H:53
virtual bool write()
Write the PPD and PMV fields.
Definition: comfort.C:424
IOobject defines the attributes of an object for which implicit objectRegistry management is supporte...
Definition: IOobject.H:92
Namespace for OpenFOAM.
const dimensionSet dimVelocity