Code_TYMPAN  4.4.0
Industrial site acoustic simulation
TYAcousticModel9613Solver.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (C) <2012-2024> <EDF-DTG> <FRANCE>
3  * This file is part of Code_TYMPAN (R).
4  * Code_TYMPAN (R) is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 3 of the License, or
7  * (at your option) any later version.
8  * Code_TYMPAN (R) is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11  * See the GNU General Public License for more details.
12  * You should have received a copy of the GNU General Public License along
13  * with Code_TYMPAN (R). If not, see <https://www.gnu.org/licenses/>.
14  */
15 
16 #include <deque>
17 #include <list>
18 #include <cmath>
19 #include <algorithm>
20 #include "Tympan/core/defines.h"
25 #include "TYSolver9613Solver.h"
26 #include "TYSolverHelper.h"
27 
29 
31  : TYAcousticModel(), _solver(solver)
32 {
34  _absoNulle.setType(SPECTRE_TYPE_ABSO); // Spectre d'absorption
35 }
36 
38 {
39  // Compute wave length
40  double c_9613_2 = 340.0;
42 }
43 
44 void TYAcousticModel9613Solver::compute(const std::deque<TYSIntersection>& tabIntersect,
45  TYTrajet9613Solver& trajet, TabPoint3D& ptsTop, TabPoint3D& ptsLeft,
46  TabPoint3D& ptsRight)
47 {
48  bool vertical = true, horizontal = false;
49  bool left = true, right = false;
50 
51  // Construction du rayon SR
52  OSegment3D rayon;
53  trajet.getPtSetPtRfromOSeg3D(rayon);
54  bool conditionFav = false;
55 
56  // Calcul des conditions de propagation suivant la direction du vent
58  assert(config->DSWindDirection >= 0 && config->DSWindDirection <= 360);
59 
60  double windRadian = DEGTORAD(config->DSWindDirection);
61  OVector3D windDirection = OVector3D(-sin(windRadian), -cos(windRadian), 0);
62  OVector3D propaDirection = rayon.toVector3D();
63  propaDirection._z = 0;
64  double angle =
65  RADTODEG(acos(windDirection.dot(propaDirection) /
66  (windDirection.norme() * propaDirection.norme()))); // Angle always between 0-180
67  assert(180 >= angle >= 0);
68  assert(180 >= config->AngleFavorable >= 0);
69 
70  if (angle <= config->AngleFavorable)
71  {
72  conditionFav = true;
73  }
74  else
75  {
76  conditionFav = false;
77  }
78 
79  // Recuperation de la source
80  tympan::AcousticSource& source = trajet.asrc;
81 
82  // Distance de la source au recepteur
83  double distance = trajet.getDistance();
84 
85  TYTabChemin9613Solver& tabChemins = trajet.getChemins();
86 
87  // Calcul du chemin direct
88  computeCheminSansEcran(tabIntersect, rayon, source, tabChemins, distance, conditionFav);
89 
90  if (ptsTop.size() > 1 || ptsLeft.size() > 1 || ptsRight.size() > 1)
91  {
92  // Calcul des parcours lateraux
93  // 1. Vertical
94  computeCheminsAvecEcran(rayon, source, ptsTop, vertical, tabChemins, distance, right);
95 
96  // 2. Horizontal gauche
97  computeCheminsAvecEcran(rayon, source, ptsLeft, horizontal, tabChemins, distance, left);
98 
99  // 3. Horizontal droite
100  computeCheminsAvecEcran(rayon, source, ptsRight, horizontal, tabChemins, distance, right);
101  }
102 
103  // Calcul des reflexions si necessaire
104  computeCheminReflexion(tabIntersect, rayon, source, tabChemins, distance);
105 
106  // Calcul la pression cumulee de tous les chemins au point de reception du trajet
107  solve(trajet);
108 
109  // Le calcul est fini pour ce trajet, on peut effacer les tableaux des chemins
110  tabChemins.clear();
111 }
112 
114  const tympan::AcousticSource& source,
115  const TabPoint3D& pts, const bool vertical,
116  TYTabChemin9613Solver& tabPaths, double distance,
117  const bool left) const
118 {
119  /* ============================================================================================================
120  In 9613, no reflexion on the ground is computed
121  ==============================================================================================================*/
122  if (pts.size() <= 1)
123  {
124  tabPaths[0].setAttenuationBarWhenNoPath(vertical, left);
125  return false;
126  }
127 
128  double dss{0.0}; // Length between source and first edge of diffraction
129  double dsr{0.0}; // Length between last edge of diffraction and receptor
130 
131  OPoint3D firstPt(pts[1]);
132  OPoint3D lastPt(pts[pts.size() - 1]);
133 
134  TYTabEtape9613Solver tabSteps;
135  double pathLength = 0.0;
136 
137  /*--- BEFORE OBSTACLE ---*/
138 
139  TYTabEtape9613Solver steps;
140  OSegment3D curSegment(ray._ptA, firstPt);
141  double tempLength = curSegment.longueur();
142 
143  bool bPathOk = addGroundSteps(ray._ptA, firstPt, source, true, steps); // Add step before obstacle
144 
145  // If a problem has occurred, stop path creation
146  if (!bPathOk)
147  {
148  return true;
149  }
150 
151  tabSteps.push_back(steps[0]); // Add source step to table of steps
152  pathLength += tempLength;
153  dss = tempLength;
154 
155  steps.clear(); // Clear steps content
156 
157  /*--- BYPASS OF THE OBSTACLE ---*/
158 
159  double width = 0.0;
160  TYEtape9613Solver step;
161 
162  for (unsigned int i = 1; i < pts.size() - 1; i++)
163  {
164  width += (OSegment3D(pts[i], pts[i + 1])).longueur();
165 
166  step._pt = pts[i];
167  step._type = TYDIFFRACTION;
168  step._Absorption = _absoNulle;
169 
170  tabSteps.push_back(step);
171  }
172 
173  pathLength += width;
174 
175  /*--- AFTER OBSTACLE ---*/
176  curSegment = OSegment3D(lastPt, ray._ptB);
177  tempLength = curSegment.longueur();
178 
179  addGroundSteps(lastPt, ray._ptB, source, false, steps);
180 
181  tabSteps.push_back(steps[0]);
182  pathLength += tempLength;
183  dsr = tempLength;
184 
185  steps.clear();
186 
187  /*--- COMPUTE SCREEN EFFECT ATTENUATION ON THE CURRENT PATH ---*/
188 
189  OSpectreOctave Dz;
190 
191  step._pt = ray._ptB;
192  step._type = TYRECEPTEUR;
193  step._Absorption = _absoNulle;
194 
195  Dz = calculAttDiffraction(ray, pathLength, dss, dsr, width, vertical);
196  step._Attenuation = Dz;
197  tabSteps.push_back(step);
198 
199  /*--- ADD PATH TO the table of paths ---*/
200 
201  TYChemin9613Solver path;
203  path.setDistance(distance);
204  path.setLongueur(pathLength);
205 
206  tabPaths.push_back(path);
207 
208  // Compute barrier attenuation
209  tabPaths[0].computeBarAttenuation(Dz, vertical, left);
210 
211  // Build equivalent path for rays
212  path.build_eq_path(tabSteps);
213 
214  tabSteps.clear();
215  steps.clear();
216 
217  return true;
218 }
219 
221  const tympan::AcousticSource& source, const bool& fromSource,
222  TYTabEtape9613Solver& steps) const
223 {
224  bool res = true;
225 
226  TYEtape9613Solver curStep;
227 
228  // === BUILD DIRECT TRIP ptStart-ptEnd
229  curStep._pt = ptStart;
230 
231  if (fromSource) // If we start from source, its directivity is considered
232  {
233  curStep._type = TYSOURCE;
234  curStep._Absorption =
235  source.directivity->lwAdjustment(OVector3D(ptStart, ptEnd), ptStart.distFrom(ptEnd));
236  }
237  else
238  {
239  curStep._type = TYDIFFRACTION;
240  curStep._Absorption = _absoNulle;
241  }
242 
243  steps.push_back(curStep);
244 
245  return res;
246 }
247 
248 void TYAcousticModel9613Solver::computeCheminSansEcran(const std::deque<TYSIntersection>& tabIntersect,
249  const OSegment3D& ray,
250  const tympan::AcousticSource& source,
251  TYTabChemin9613Solver& TabChemin, double distance,
252  bool conditionFav) const
253 {
254  /*
255  COMPUTATION FOR A ROUTE WITHOUT OBSTACLE CONSISTS OF ONE DIRECT PATH
256  */
257  TYTabEtape9613Solver tabEtapes;
258 
259  // Compute mean slope on source-receptor route
260  OSegment3D segMeanSlope;
261  meanSlope(ray, segMeanSlope);
262 
263  OPoint3D S2D{ray._ptA._x, ray._ptA._y, 0.0};
264  OPoint3D R2D{ray._ptB._x, ray._ptB._y, 0.0};
265  OSegment3D SR2D{S2D, R2D};
266  double dp = SR2D.longueur();
267 
268  TYTabEtape9613Solver Etapes;
269  double Gs{0.0}, Gm{0.0}, Gr{0.0};
270  double hs{0.0}, hr{0.0};
271 
272  computeSegmentEdgesHeights(hs, hr, segMeanSlope, ray);
273 
274  addGroundSteps(ray._ptA, ray._ptB, source, true, Etapes);
275  getGroundfactors(tabIntersect, SR2D, hs, hr, Gs, Gm, Gr);
276 
277  // Add direct path
278  TYChemin9613Solver chemin;
280  tabEtapes.push_back(Etapes[0]); // Add direct step
281  chemin.setLongueur(distance); // In this case, lenght = source/receptor distance
282  chemin.setDistance(distance);
283  chemin.calcAttenuation(tabEtapes, *_pSolverAtmos, dp, hs, hr, Gs, Gm, Gr);
284  TabChemin.push_back(chemin); // Add path in array of paths
285 
286  tabEtapes.clear(); // Empty array of steps
287 }
288 
289 bool TYAcousticModel9613Solver::getGroundfactors(const std::deque<TYSIntersection>& tabIntersect,
290  const OSegment3D& ray2D, double hs, double hr, double& Gs,
291  double& Gm, double& Gr) const
292 {
293  double heightRatio = 30.0;
294 
295  bool res = true;
296 
297  // === CONSTRUCTION OF DIRECT ROUTE ABOVE HORIZONTAL PLANE
298  OPoint3D ptStartProj = ray2D._ptA; // PtDebut projected on horizontal plane
299  OPoint3D ptEndProj = ray2D._ptB; // PtFin projected on horizontal plane
300 
301  // === COMPUTE SOURCE, MIDDLE AND RECEPTOR ZONES
302  OVector3D SR{ray2D._ptA, ray2D._ptB};
303  double dp = SR.norme(); // Distance in meter, between source and receptor, projected on ground plan
304  SR.normalize();
305  OPoint3D ptGSrcZone;
306  OPoint3D ptGRcpZone;
307  OPoint3D ptGMidZone;
308 
309  // Source zone must not exceed receptor
310  if (heightRatio * hs < dp)
311  {
312  ptGSrcZone = ptStartProj + SR * heightRatio * hs;
313  }
314  else
315  {
316  ptGSrcZone = ptStartProj + SR * dp;
317  }
318 
319  // Receptor zone must not exceed source
320  if (heightRatio * hr < dp)
321  {
322  ptGRcpZone = ptEndProj + (-1.0) * SR * heightRatio * hr;
323  }
324  else
325  {
326  ptGRcpZone = ptEndProj + (-1.0) * SR * dp;
327  }
328 
329  // === COMPUTE GROUND FACTOR FOR EACH ZONE
330  double GZone{0.0}, dpZone{0.0};
331  computeGZone(ptStartProj, ptGSrcZone, GZone, dpZone, tabIntersect);
332  if (dpZone != 0.0)
333  {
334  Gs = GZone / dpZone;
335  }
336  else
337  {
338  Gs = 0.5;
339  }
340  computeGZone(ptGRcpZone, ptEndProj, GZone, dpZone, tabIntersect);
341  if (dpZone != 0.0)
342  {
343  Gr = GZone / dpZone;
344  }
345  else
346  {
347  Gr = 0.5;
348  }
349  computeGZone(ptGSrcZone, ptGRcpZone, GZone, dpZone, tabIntersect);
350  if (dpZone != 0.0)
351  {
352  Gm = GZone / dpZone;
353  }
354  else
355  {
356  Gm = 0.5;
357  }
358 
359  return res;
360 }
361 
362 bool TYAcousticModel9613Solver::getGroundfactors(const std::deque<TYSIntersection>& tabIntersectUpSegment,
363  const std::deque<TYSIntersection>& tabIntersectDownSegment,
364  const OSegment3D& SO2D, const OSegment3D& OR2D, double hs,
365  double hr, double& Gs, double& Gm, double& Gr) const
366 {
367  double heightRatio = 30.0;
368  bool res = true;
369 
370  // Construction of points from segments parameters
371  OPoint3D ptSrc2D = SO2D._ptA;
372  OPoint3D ptO2D = SO2D._ptB;
373  OPoint3D ptRcp2D = OR2D._ptB;
374 
375  // Computation of 2D distance and construction of unit vectors
376  OVector3D SO{SO2D._ptA, SO2D._ptB};
377  double dpSO =
378  SO.norme(); // Distance in meter, between source and reflexion point, projected on ground plan
379  SO.normalize();
380 
381  OVector3D OR{OR2D._ptA, OR2D._ptB};
382  double dpOR =
383  OR.norme(); // Distance in meter, between reflexion point and receptor, projected on ground plan
384  OR.normalize();
385 
386  double dp = dpSO + dpOR; // Distance in meter, between source and receptor, projected on ground plan
387 
388  // === COMPUTE GROUND FACTOR FOR EACH ZONE
389  OPoint3D ptGSrcZone;
390  OPoint3D ptGRcpZone;
391  OPoint3D ptGMidZone;
392 
393  bool bPtGSrcZoneInSO{false}; // True if ptGSrcZone in [SO] segment
394  bool bPtGRcpZoneInOR{false}; // True if ptGRcpZone in [OR] segment
395 
396  // == COMPUTE GROUND FACTOR FOR SOURCE ZONE
397  if (heightRatio * hs < dpSO)
398  {
399  // ptGSrcZone belongs to [SO]
400  bPtGSrcZoneInSO = true;
401  ptGSrcZone = ptSrc2D + (SO)*heightRatio * hs;
402  }
403  else if (heightRatio * hs < dp)
404  {
405  // ptGSrcZone belongs to [OR]
406  ptGSrcZone = ptO2D + (OR) * (heightRatio * hs - dpSO);
407  }
408  else
409  { // Source Zone must not exceed receptor
410  ptGSrcZone = ptO2D + (OR)*dpOR;
411  }
412 
413  // Compute Gs
414  double GZone{0.0}, dpZone{0.0};
415  if (bPtGSrcZoneInSO)
416  {
417  computeGZone(ptSrc2D, ptGSrcZone, GZone, dpZone, tabIntersectUpSegment);
418  }
419  else
420  {
421  double Gs1{0.0}, dp1{0.0}, Gs2{0.0}, dpOGs{0.0};
422  computeGZone(ptSrc2D, ptO2D, Gs1, dp1, tabIntersectUpSegment); // Gs1 = G(Src2D->ptO2D) / dpSO
423  computeGZone(ptO2D, ptGSrcZone, Gs2, dpOGs, tabIntersectDownSegment); // Gs2 = G(ptO2D->Gs2D) / dpOGs
424  GZone = Gs1 + Gs2;
425  dpZone = dp1 + dpOGs;
426  }
427 
428  if (dpZone != 0)
429  {
430  Gs = GZone / dpZone;
431  }
432  else
433  {
434  Gs = 0.5;
435  }
436 
437  // == COMPUTE GROUND FACTOR FOR RECEPTOR ZONE
438  if (heightRatio * hr < dpOR)
439  {
440  // ptGRcpZone belongs to [OR]
441  bPtGRcpZoneInOR = true;
442  ptGRcpZone = ptRcp2D + (-1.0) * OR * heightRatio * hr;
443  }
444  else if (heightRatio * hr < dp)
445  {
446  // ptGSrcZone belongs to [SO]
447  ptGRcpZone = ptO2D + (-1.0) * SO * (heightRatio * hr - dpOR);
448  }
449  else
450  { // Source Zone must not exceed receptor
451  ptGRcpZone = ptO2D + (-1.0) * SO * dpSO;
452  }
453 
454  // Compute Gr
455  dpZone = 0.0;
456  if (bPtGRcpZoneInOR)
457  {
458  computeGZone(ptGRcpZone, ptRcp2D, GZone, dpZone, tabIntersectDownSegment);
459  }
460  else
461  {
462  double Gr1{0.0}, dp2{0.0}, Gr2{0.0}, dpGrO{0.0};
463  computeGZone(ptGRcpZone, ptO2D, Gr1, dpGrO, tabIntersectUpSegment); // Gr1 = G(Gr2D->ptO2D) / dpGrO
464  computeGZone(ptO2D, ptRcp2D, Gr2, dp2, tabIntersectDownSegment); // Gr2 = G(ptO2D->ptRcp2D) / dpOR
465 
466  GZone = Gr1 + Gr2;
467  dpZone = dpGrO + dp2;
468  }
469 
470  if (dpZone != 0)
471  {
472  Gr = GZone / dpZone;
473  }
474  else
475  {
476  Gr = 0.5;
477  }
478 
479  // == COMPUTE GROUND FACTOR FOR MIDDLE ZONE
480  // If GSrc and GRcp belong to [SO]
481  if (bPtGSrcZoneInSO && !bPtGRcpZoneInOR)
482  {
483  computeGZone(ptGSrcZone, ptGRcpZone, GZone, dpZone, tabIntersectUpSegment);
484  }
485  // Else, if GSrc and GRcp belong to [OR]
486  else if (!bPtGSrcZoneInSO && bPtGRcpZoneInOR)
487  {
488  computeGZone(ptGSrcZone, ptGRcpZone, GZone, dpZone, tabIntersectDownSegment);
489  }
490  // Else, if GSrc belongs to [SO], therefore GRcp belongs to [OR]
491  else if (bPtGSrcZoneInSO)
492  {
493  double Gm1{0.0}, dpm1{0.0}, Gm2{0.0}, dpm2{0.0};
494  computeGZone(ptGSrcZone, ptO2D, Gm1, dpm1, tabIntersectUpSegment);
495  computeGZone(ptO2D, ptGRcpZone, Gm2, dpm2, tabIntersectDownSegment);
496  GZone = Gm1 + Gm2;
497  dpZone = dpm1 + dpm2;
498  }
499  // Else if GSrc belongs to [OR], therefore GRcp belongs to [SO]
500  else
501  {
502  double Gm1{0.0}, dpm1{0.0}, Gm2{0.0}, dpm2{0.0};
503  computeGZone(ptGRcpZone, ptO2D, Gm1, dpm1, tabIntersectUpSegment);
504  computeGZone(ptO2D, ptGSrcZone, Gm2, dpm2, tabIntersectDownSegment);
505  GZone = Gm1 + Gm2;
506  dpZone = dpm1 + dpm2;
507  }
508 
509  if (dpZone != 0)
510  {
511  Gm = GZone / dpZone;
512  }
513  else
514  {
515  Gm = 0.5;
516  }
517 
518  return res;
519 }
520 
522  const OSpectreOctave& Abar_left,
523  const OSpectreOctave& Abar_right)
524 {
525  OSpectreOctave Abar;
526  for (unsigned int i = 0; i < TY_SPECTRE_OCT_NB_ELMT; i++)
527  {
528  Abar.getTabValReel()[i] = -10 * log10(pow(10, -0.1 * Abar_top.getTabValReel()[i]) +
529  pow(10, -0.1 * Abar_left.getTabValReel()[i]) +
530  pow(10, -0.1 * Abar_right.getTabValReel()[i]));
531  if (Abar.getTabValReel()[i] < 0)
532  {
533  Abar.getTabValReel()[i] = 0;
534  }
535  }
536  return Abar;
537 }
538 
539 bool TYAcousticModel9613Solver::computeGZone(const OPoint3D& ptDebut, const OPoint3D& ptFin, double& GZone,
540  double& dpZone,
541  const std::deque<TYSIntersection>& tabIntersect) const
542 {
543  bool ret = true;
544  OSegment3D segZone(ptDebut, ptFin);
545  OVector3D DF(ptDebut, ptFin);
546  std::unordered_map<OVector3D, double, OVector3DHash> mapResultSegFactorG;
547 
548  // Loop on intersections of Topography triangles with EV plane.
549  // For each triangle, we search the intersection between the intersecting segment and the zone segment.
550  // The intersecting segment has an homogeneous ground factor G.
551  // It is used to compute a balanced ground factor over the zone segment.
552  size_t nbTriangles = tabIntersect.size();
553  double currentG = 0.0;
554  GZone = 0.0;
555  dpZone = 0.0;
556 
557  // Edges of current intersecting segment
558  OPoint3D ptDebutResult;
559  OPoint3D ptFinResult;
560 
561  for (unsigned int i = 0; i < nbTriangles; i++)
562  {
563  TYSIntersection inter = tabIntersect[i];
564 
565  // If triangle is not topography or does not intersect EV plane, then continue with following triangle
566  if ((inter.isInfra) || !(inter.bIntersect[0]))
567  {
568  continue;
569  }
570 
571  OSegment3D currentSeg = tabIntersect[i].segInter[0];
572  currentG = tabIntersect[i].material->get_ISO9613_G();
573 
574  // We build AB segment the projection of the topography segment on the horizontal plane
575  OPoint3D ptDebutCurrentProj{currentSeg._ptA._x, currentSeg._ptA._y, 0.0};
576  OPoint3D ptFinCurrentProj{currentSeg._ptB._x, currentSeg._ptB._y, 0.0};
577 
578  OSegment3D segAB{ptDebutCurrentProj, ptFinCurrentProj};
579  OVector3D AB{ptDebutCurrentProj, ptFinCurrentProj};
580  // Orientate AB segment as zone segment DF
581  if (AB.scalar(DF) <= 0)
582  {
583  segAB = segAB.swap();
584  }
585  OVector3D AD(segAB._ptA, segZone._ptA);
586  OVector3D AF(segAB._ptA, segZone._ptB);
587  OVector3D BD(segAB._ptB, segZone._ptA);
588  OVector3D BF(segAB._ptB, segZone._ptB);
589 
590  bool intersect = true;
591 
592  // If A belongs to zone segment DF
593  if (AD.scalar(AF) <= 0)
594  {
595  // then A is the starting edge of the intersecting segment
596  ptDebutResult = segAB._ptA;
597 
598  // If B belongs to zone segment DF
599  if (BD.scalar(BF) <= 0)
600  {
601  // then B is the ending edge of the intersecting segment
602  ptFinResult = segAB._ptB;
603  }
604  else
605  // else F is the ending edge of the intersecting segment
606  {
607  ptFinResult = segZone._ptB;
608  }
609  }
610  else
611  {
612  // Else A does not belong to zone segment DF
613  // If B does not belong to zone segment either
614  if (BD.scalar(BF) >= 0)
615  {
616  // and if A and B are from each side of zone segment
617  if (AD.scalar(BF) <= 0)
618  {
619  // then intersecting segment is the zone segment DF itself
620  ptDebutResult = segZone._ptA;
621  ptFinResult = segZone._ptB;
622  }
623  else
624  {
625  // Else A and B are on the same side of segment zone, so no intersection
626  intersect = false;
627  }
628  }
629  else
630  // Else B belong to segment zone DF but not A
631  {
632  ptDebutResult = segZone._ptA;
633  ptFinResult = segAB._ptB;
634  }
635  }
636 
637  if (intersect)
638  {
639  OVector3D result{ptDebutResult, ptFinResult};
640  auto it = mapResultSegFactorG.find(result);
641  if (it != mapResultSegFactorG.end())
642  {
643  GZone = GZone + 0.5 * result.norme() * (currentG - it->second);
644  }
645  else
646  {
647  GZone = GZone + result.norme() * currentG;
648  dpZone += result.norme();
649  }
650  mapResultSegFactorG[result] = currentG;
651  }
652  }
653 
654  return ret;
655 }
656 
657 void TYAcousticModel9613Solver::computeCheminReflexion(const std::deque<TYSIntersection>& tabIntersect,
658  const OSegment3D& ray,
659  const tympan::AcousticSource& source,
660  TYTabChemin9613Solver& TabChemins,
661  double distance) const
662 {
663  if (!_useReflex)
664  {
665  return;
666  }
667 
668  OSegment3D segInter;
669  OSegment3D rayonTmp;
670  OPoint3D ptSym;
671  OSpectreOctave SpectreAbso;
672 
673  OSegment3D seg; // Image source -> receptor segment
674  OSegment3D upwardSeg; // Source -> reflexion point segment
675  OSegment3D downwardSeg; // Reflexion point -> receptor segment
676 
677  OPoint3D pt; // Intersection (reflexion) point
678 
679  size_t nbFaces = tabIntersect.size();
680 
681  // For each face test reflexion
682  for (unsigned int i = 0; i < nbFaces; i++)
683  {
684  TYSIntersection inter = tabIntersect[i];
685 
686  // If face cannot interact skip it
687  if ((!inter.isInfra) || !(inter.bIntersect[1]))
688  {
689  continue;
690  }
691 
692  segInter = inter.segInter[1];
693 
694  // Compute symmetric of A with respect to the segment
695  segInter.symetrieOf(ray._ptA, ptSym); // We don't deal with this function return value
696  seg._ptA = ptSym;
697  seg._ptB = ray._ptB; // Image source -> receptor segment
698 
699  if (segInter.intersects(seg, pt, TYSEUILCONFONDUS))
700  {
701  // Construction of reflexion point -> source segment
702  upwardSeg._ptA = ray._ptA;
703  upwardSeg._ptB = pt;
704  // Construction of reflexion point -> receptor segment
705  downwardSeg._ptA = upwardSeg._ptB;
706  downwardSeg._ptB = ray._ptB;
707 
708  bool intersect = false;
709  size_t j = 0;
710 
711  // If we cross another face, which can be topography, the reflexion path is not taken into account
712  while ((j < nbFaces) && (!intersect))
713  {
714  if (j == i)
715  {
716  j++;
717  continue; // If face cannot interact skip it
718  }
719 
720  segInter = tabIntersect[j].segInter[1];
721 
722  // We test whether segInter intersects upward segment or
723  // downward segment in global plane.
724  // Point pt is not use, we only care about testing intersection
725  if ((segInter.intersects(upwardSeg, pt, TYSEUILCONFONDUS)) ||
726  (segInter.intersects(downwardSeg, pt, TYSEUILCONFONDUS)))
727  {
728  // Intersection found, exit from the loop
729  intersect = true;
730  break;
731  }
732 
733  j++;
734  }
735 
736  // If reflected path is not intersected, reflexion can be computed
737  if (!intersect)
738  {
739  SpectreAbso = dynamic_cast<tympan::AcousticBuildingMaterial*>(inter.material)->spectrum;
740 
741  TYTabEtape9613Solver tabEtapes;
742 
743  double pathLength = upwardSeg.longueur() + downwardSeg.longueur();
744 
745  TYEtape9613Solver Etape;
746  // First step : from source to reflexion point
747  Etape._pt = ray._ptA;
748  Etape._type = TYSOURCE;
749  Etape._Absorption = source.directivity->lwAdjustment(
750  OVector3D(upwardSeg._ptA, upwardSeg._ptB),
751  upwardSeg.longueur()); // Directivity factor toward receptor image
752 
753  tabEtapes.push_back(Etape);
754 
755  // Second step : from reflexion point to end of ray
756  Etape._pt = downwardSeg._ptA;
757  Etape._type = TYREFLEXION;
758  Etape._Absorption = SpectreAbso;
759 
760  tabEtapes.push_back(Etape);
761 
762  // Compute mean slope on source-receptor route
763  OSegment3D segMeanSlope;
764  meanSlope(ray, segMeanSlope);
765 
766  OPoint3D S2D{upwardSeg._ptA._x, upwardSeg._ptA._y, 0.0};
767  OPoint3D O2D{upwardSeg._ptB._x, upwardSeg._ptB._y, 0.0};
768  OPoint3D R2D{downwardSeg._ptB._x, downwardSeg._ptB._y, 0.0};
769  OSegment3D raySO{S2D, O2D};
770  OSegment3D rayOR{O2D, R2D};
771  double dp = raySO.longueur() + rayOR.longueur();
772 
773  TYTabEtape Etapes;
774  double Gs{0.0}, Gm{0.0}, Gr{0.0};
775  double hs{0.0}, hr{0.0};
776 
777  computeSegmentEdgesHeights(hs, hr, segMeanSlope, ray);
778 
779  // Compute intersecting segments for reflected ray
780  std::deque<TYSIntersection> tabIntersectUpSegment, tabIntersectDownSegment;
781  _solver.selectFaces(tabIntersectUpSegment, upwardSeg, source.volume_id);
782  _solver.selectFaces(tabIntersectDownSegment, downwardSeg, source.volume_id);
783 
784  // Compute ground factors for reflected ray
785  getGroundfactors(tabIntersectUpSegment, tabIntersectDownSegment, raySO, rayOR, hs, hr, Gs, Gm,
786  Gr);
787 
788  TYChemin9613Solver Chemin;
790  Chemin.setLongueur(pathLength);
791  Chemin.setDistance(distance);
792  Chemin.calcAttenuation(tabEtapes, *_pSolverAtmos, dp, hs, hr, Gs, Gm, Gr);
793 
794  TabChemins.push_back(Chemin); // Put the reflected path in paths table
795  tabEtapes.clear();
796  }
797  }
798  }
799 }
800 
802 {
803  // C3 = (1 + (5 * lambda / width)^2) / (1 / 3 + (5 * lambda / width)^2)
804 
806  OSpectreOctave opLambda;
807 
808  if (width < 0.5)
809  {
810  C3.setDefaultValue(1.0);
811  }
812  else
813  {
814  const double oneThird = 1.0 / 3.0;
815 
816  opLambda = _lambda * (5.0 / width); // (5*lambda/e)
817  opLambda = opLambda * opLambda; // (5*lambda/e)^2
818 
819  C3 = opLambda + 1.0; // 1 + (5*lambda/e)^2
820  C3 = C3.div(opLambda + oneThird); // (1 + (5*lambda/e)^2) / (1/3 + (5*lambda/e)^2)
821  }
822 
823  C3.setType(SPECTRE_TYPE_AUTRE); // Neither Attenuation, nor Absorption
824 
825  return C3;
826 }
827 
829  const double& dss, const double& dsr,
830  const double& width,
831  const bool& vertical) const
832 {
833  double rd;
834 
835  OSpectreOctave s, Dz;
836 
837  OSpectreOctave C3 = calculC3(width); // Corrective factor linked with screen width
838 
839  double C2{20.0};
840 
841  rd = ray.longueur();
842 
843  double z = re - rd; // Path-length difference
844  z = z <= 0 ? 0.0 : z;
845 
846  double Kmeteo = 1.0;
847  if (z > 0.0 && vertical)
848  {
849  Kmeteo = exp(-(1.0 / 2000.0) * sqrt(dss * dsr * rd / (2 * z)));
850  }
851 
852  // Attenuation brought by diffraction Dz = 10 * log [3 + (C2 / lambda) * C3 * delta * Kmeteo] dB
853 
854  s = _lambda.invMult(C2 * z); // =C2*z/lambda
855  s = s * C3 * Kmeteo; // C2*z*C3*Kmeteo/lambda
856  s = s + 3.0;
857  Dz = s.log() * 10.0;
858  // If diffraction occurs in vertical plane (horizontal edge)
859  // minimal and amaximal attenuations are limited respectively
860  // to 0 dB and 20 or 25 dB (whether screen is thin or wide).
861  if (vertical)
862  {
863  Dz = limAttDiffraction(Dz, C3);
864  }
865 
866  return Dz;
867 }
868 
870  const OSpectreOctave& C) const
871 {
873 
874  double lim20dB = 20.0;
875  double lim25dB = 25.0;
876  double lim0dB = 0.0;
877 
878  double valeur;
879 
880  for (unsigned int i = 0; i < sNC.getNbValues(); i++)
881  {
882  valeur = sNC.getTabValReel()[i];
883 
884  valeur = valeur < lim0dB ? lim0dB : valeur; // L'attenuation ne peut etre inferieure a 0 dB
885 
886  if ((C.getTabValReel()[i] - 1) <= 1e-2) // Comportement ecran mince
887  {
888  valeur = valeur > lim20dB ? lim20dB : valeur;
889  }
890  else // Comportement ecran epais ou multiple
891  {
892  valeur = valeur > lim25dB ? lim25dB : valeur;
893  }
894 
895  s.getTabValReel()[i] = valeur;
896  }
897 
898  return s;
899 }
900 
902 {
903  // Get results for each path
904 #ifdef _DEBUG
905  std::vector<PathResults> pathsResults;
906 #endif
907  PathResults currentPathResults;
908  // Global sound level pressure
909  OSpectreOctave& SLp = trajet.getSpectreOct();
910  SLp.setType(SPECTRE_TYPE_LP); // Receptor spectrum is a pressure spectrum
912 
913  for (unsigned int i = 0; i < trajet.getNbChemins(); i++)
914  {
915  currentPathResults.path_id = i;
916  currentPathResults.pathType = trajet.getChemin(i).getType();
917  float minSRDistance = config->MinSRDistance;
918  double longueur = (minSRDistance > trajet.getChemin(i).getLongueur())
919  ? minSRDistance
920  : trajet.getChemin(i).getLongueur();
921 
922  // Screen and ground paths results are held by direct path
923  if (currentPathResults.pathType == TYTypeChemin::CHEMIN_ECRAN)
924  {
925  continue;
926  }
927  // Direct Ray computations
929 
930  // Compute attenuations
931  currentPathResults.Adiv = OSpectreOctave(20.0 * log10(longueur) + 11.0);
932  currentPathResults.Aatm = trajet.getChemin(i).getAttenuation(TYTypeAttenuation::ATTENUATION_ATM);
933  currentPathResults.Agr_s = trajet.getChemin(i).getAttenuation(TYTypeAttenuation::ATTENUATION_GND_S);
934  currentPathResults.Agr_r = trajet.getChemin(i).getAttenuation(TYTypeAttenuation::ATTENUATION_GND_R);
935  currentPathResults.Agr_m = trajet.getChemin(i).getAttenuation(TYTypeAttenuation::ATTENUATION_GND_M);
936  currentPathResults.Dz_top = trajet.getChemin(i).getAttenuation(TYTypeAttenuation::DZ_TOP);
937  currentPathResults.Dz_left = trajet.getChemin(i).getAttenuation(TYTypeAttenuation::DZ_LEFT);
938  currentPathResults.Dz_right = trajet.getChemin(i).getAttenuation(TYTypeAttenuation::DZ_RIGHT);
939  currentPathResults.Abar_top =
941  currentPathResults.Abar_left =
943  currentPathResults.Abar_right =
945  currentPathResults.Abar = computeEffectiveBarAttenuation(
946  currentPathResults.Abar_top, currentPathResults.Abar_left, currentPathResults.Abar_right);
947 
948  currentPathResults.A = currentPathResults.Adiv + currentPathResults.Aatm + currentPathResults.Agr_s +
949  currentPathResults.Agr_r + currentPathResults.Agr_m + currentPathResults.Abar;
950 
951  // Get source power level and source directivity LW + DC
952  currentPathResults.LW = OSpectreOctave(trajet.asrc.spectrum).round(); // Round spectrum to 2 digits
953  // for compliance with ISO
954  // TR 17534-3 values
955 
956  // Get source directivity correction
957  currentPathResults.Dc =
959 
960  // If path is reflected one, then compute LW_image
961  if (currentPathResults.pathType == TYTypeChemin::CHEMIN_REFLEX)
962  {
963  currentPathResults.LW =
964  currentPathResults.LW +
966  }
967  currentPathResults.L = currentPathResults.LW + currentPathResults.Dc - currentPathResults.A;
968  currentPathResults.L.setType(SPECTRE_TYPE_LP); // L is a pressure spectrum
969  SLp = SLp.sumdB(currentPathResults.L);
970 
971 #ifdef _DEBUG
972  pathsResults.push_back(currentPathResults);
973 #endif
974  }
975 
976  // Trace results
977  // TODO Remove trace or keep it only in Debug
978 #ifdef _DEBUG
980  pathsResults, SLp,
981  *_pSolverAtmos); // Export results in a csv file for comparison with 17534-3 standard
982 #endif
983 
984  trajet.build_tab_rays();
985  trajet.reset(); // Erase paths array to (try to) spare memory
986  return true;
987 }
988 
989 bool TYAcousticModel9613Solver::computeSegmentEdgesHeights(double& hauteurA, double& hauteurB,
990  const OSegment3D& meanSlope,
991  const OSegment3D& ray) const
992 {
993  bool res = true;
994  hauteurA = ray._ptA._z - meanSlope._ptA._z;
995  hauteurB = ray._ptB._z - meanSlope._ptB._z;
996  return res;
997 }
double RADTODEG(double a)
Converts an angle from radians to degrees.
Definition: 3d.h:137
#define TYSEUILCONFONDUS
Definition: 3d.h:47
double DEGTORAD(double a)
Converts an angle from degrees to radians.
Definition: 3d.h:126
std::vector< OPoint3D > TabPoint3D
Definition: 3d.h:483
NxReal s
Definition: NxVec3.cpp:317
std::deque< TYChemin9613Solver > TYTabChemin9613Solver
TYChemin collection.
std::deque< TYEtape9613Solver > TYTabEtape9613Solver
TYEtape collection.
std::deque< TYEtape > TYTabEtape
TYEtape collection.
Definition: TYEtape.h:113
@ TYREFLEXION
Definition: acoustic_path.h:25
@ TYRECEPTEUR
Definition: acoustic_path.h:29
@ TYSOURCE
Definition: acoustic_path.h:28
@ TYDIFFRACTION
Definition: acoustic_path.h:24
double _y
y coordinate of OCoord3D
Definition: 3d.h:283
double _z
z coordinate of OCoord3D
Definition: 3d.h:284
double _x
x coordinate of OCoord3D
Definition: 3d.h:282
The 3D point class.
Definition: 3d.h:487
double distFrom(const OPoint3D &pt) const
Computes the distance from this point to another.
Definition: 3d.cpp:371
Class to define a segment.
Definition: 3d.h:1141
virtual double longueur() const
Return the segment length.
Definition: 3d.cpp:1238
virtual int symetrieOf(const OPoint3D &pt, OPoint3D &ptSym) const
Return the symmetrical of a point.
Definition: 3d.cpp:1243
OPoint3D _ptA
Point A of the segment.
Definition: 3d.h:1253
virtual OVector3D toVector3D() const
Build a OVector3D from a segment used for the direction of the sources.
Definition: 3d.h:1240
virtual int intersects(const OSegment3D &seg, OPoint3D &pt, double seuilConfondus) const
Return the intersection point with another segment.
Definition: 3d.cpp:1267
OPoint3D _ptB
Point B of the segment.
Definition: 3d.h:1255
OSpectreAbstract & log(const double &base=10.0) const
Compute the log base n of this spectrum (n=10 by default).
Definition: spectre.cpp:409
unsigned int getNbValues() const
Number of values in the spectrum.
Definition: spectre.cpp:187
OSpectreAbstract & invMult(const double &coefficient=1.0) const
Division of a double constant by this spectrum.
Definition: spectre.cpp:350
void setType(TYSpectreType type)
Set the spectrum type.
Definition: spectre.h:153
OSpectreAbstract & div(const OSpectreAbstract &spectre) const
Division of two spectrums.
Definition: spectre.cpp:278
void setDefaultValue(const double &valeur=TY_SPECTRE_DEFAULT_VALUE)
Definition: spectre.cpp:202
OSpectreAbstract & round()
Definition: spectre.cpp:578
OSpectreAbstract & sumdB(const OSpectreAbstract &spectre) const
Energetic sum of two spectrums.
Definition: spectre.cpp:176
static OSpectreOctave getLambda(const double &c)
Definition: spectre.cpp:1605
static OSpectreOctave getEmptyLinSpectre(const double &valInit=1.0E-20)
Create a physical quantity spectrum.
Definition: spectre.cpp:1630
double * getTabValReel() override
Get an array of the real values of the spectrum.
Definition: spectre.h:614
The 3D vector class.
Definition: 3d.h:298
double norme() const
Computes the length of this vector.
Definition: 3d.cpp:215
double scalar(const OVector3D &vector) const
Performs the scalar product between this object and another vector.
Definition: 3d.cpp:210
double dot(const OVector3D &v)
dot product (assuming an orthonormal reference frame)
Definition: 3d.h:362
void computeCheminSansEcran(const std::deque< TYSIntersection > &tabIntersect, const OSegment3D &rayon, const tympan::AcousticSource &source, TYTabChemin9613Solver &TabChemins, double distance, bool conditionFav=false) const
Compute the main path between source and receptor. In 9613 solver, this path includes all attenuation...
OSpectreOctave calculC3(const double &epaisseur) const
Compute the spectrum of the C3 factor used in the diffraction calculation.
OSpectreOctave calculAttDiffraction(const OSegment3D &ray, const double &re, const double &dss, const double &dsr, const double &width, const bool &vertical) const
Compute the attenuation from the diffraction on the screen.
virtual bool computeCheminsAvecEcran(const OSegment3D &rayon, const tympan::AcousticSource &source, const TabPoint3D &pts, const bool vertical, TYTabChemin9613Solver &TabChemins, double distance, const bool left) const
Compute barrier attenuation effect on the direct path for the considered geometrical path (top,...
bool solve(TYTrajet9613Solver &trajet)
Compute the source contributions to the receptor point.
bool addGroundSteps(const OPoint3D &ptStart, const OPoint3D &ptEnd, const tympan::AcousticSource &source, const bool &fromSource, TYTabEtape9613Solver &Etapes) const
Compute the different steps from a point to another via a reflection and a direct view.
TYAcousticModel9613Solver(TYSolver9613Solver &solver)
void computeWaveLength() override
Compute the wave length for the 9613Solver.
TYSolver9613Solver & _solver
Reference to the solver.
bool computeGZone(const OPoint3D &ptDebut, const OPoint3D &ptFin, double &GZone, double &dpZone, const std::deque< TYSIntersection > &tabIntersect) const
Compute GZone and dpZone for the segment between ptDebut and ptFin.
bool computeSegmentEdgesHeights(double &hauteurA, double &hauteurB, const OSegment3D &meanSlope, const OSegment3D &ray) const
Compute heights relative to real ground, of the edges of a segment.
OSpectreOctave limAttDiffraction(const OSpectreOctave &sNC, const OSpectreOctave &C) const
Limit the screen attenuation value with a frequency dependent criteria.
OSpectreOctave computeEffectiveBarAttenuation(const OSpectreOctave &Abar_top, const OSpectreOctave &Abar_left, const OSpectreOctave &Abar_right)
void compute(const std::deque< TYSIntersection > &tabIntersect, TYTrajet9613Solver &trajet, TabPoint3D &ptsTop, TabPoint3D &ptsLeft, TabPoint3D &ptsRight)
Main entry point, trigger acoustic computations.
void computeCheminReflexion(const std::deque< TYSIntersection > &tabIntersect, const OSegment3D &ray, const tympan::AcousticSource &source, TYTabChemin9613Solver &TabChemins, double distance) const
Compute the list of path generated by reflection on the vertical walls.
bool getGroundfactors(const std::deque< TYSIntersection > &tabIntersect, const OSegment3D &ray2D, double hs, double hr, double &Gs, double &Gm, double &Gr) const
Get ground factors for source, middle and receptor zones.
Acoustic model for the considered solver.
std::unique_ptr< AtmosphericConditions > _pSolverAtmos
void meanSlope(const OSegment3D &director, OSegment3D &slope) const
Create a segment corresponding to the projection of "director" segment on the ground.
Representation of one of the most optimal path between source and receptor: S—>R. The class TYChemin ...
OSpectreOctave & getAttenuation(const TYTypeAttenuation type)
Return attenuation of the path of the type.
void calcAttenuation(const TYTabEtape9613Solver &tabEtapes, const AtmosphericConditions &atmos, double dp=0.0, double hs=0.0, double hr=0.0, double Gs=0.5, double Gm=0.5, double Gr=0.5)
Compute the global attenuation on the path.
double getLongueur()
Get/Set the path length.
Definition: TYChemin.h:97
const TYTypeChemin getType() const
Return the path type.
Definition: TYChemin.h:147
void setType(const TYTypeChemin &type)
Change the path type.
Definition: TYChemin.h:137
void setDistance(const double &distance)
Definition: TYChemin.h:128
void setLongueur(const double &longueur)
Definition: TYChemin.h:106
void build_eq_path(const T &tabEtapes)
build an acoustic_path from the tab of etapes
Definition: TYChemin.h:156
OSpectreOctave _Attenuation
attenuation Spectrum
OSpectreOctave _Absorption
absorption Spectrum
OPoint3D _pt
The starting point of this step.
Definition: TYEtape.h:109
ACOUSTIC_EVENT_TYPES _type
Acoustic event type.
Definition: TYEtape.h:108
static void exportResults17534(const std::vector< PathResults > pathsResults, const OSpectreOctave &SLp, const AtmosphericConditions &atmos)
void selectFaces(std::deque< TYSIntersection > &tabIntersect, const OSegment3D &rayon, const string &sourceVolumeId)
Delegate to _faceSelector the build of the array of intersections.
Definition: TYSolver.cpp:142
This class TYTrajet (journey) links a couple Source-Receptor and a collection of paths,...
size_t getNbChemins()
Return the number of path in *this (in addition to the direct path).
OSpectreOctave & getSpectreOct()
Get the spectrum in octave band at the receptor point Used to compute the pressure spectrum in octave...
void reset() override
Reset method.
TYTabChemin9613Solver & getChemins()
Return the collection of paths of *this.
TYChemin9613Solver getChemin(int index)
Return a path thanks to its index.
double getDistance()
Get/Set the distance between source and receptor.
Definition: TYTrajet.h:75
void getPtSetPtRfromOSeg3D(OSegment3D &seg) const
Definition: TYTrajet.h:115
tympan::AcousticSource & asrc
Business source.
Definition: TYTrajet.h:126
Describes building material.
Definition: entities.hpp:64
Describes an acoustic source.
Definition: entities.hpp:381
string volume_id
Volume id.
Definition: entities.hpp:391
SourceDirectivityInterface * directivity
Pointer to the source directivity.
Definition: entities.hpp:390
Spectrum spectrum
Associated spectrum.
Definition: entities.hpp:389
static LPSolverConfiguration get()
Get the configuration.
Definition: config.cpp:94
virtual Spectrum lwAdjustment(Vector direction, double distance)=0
< Pure virtual method to return directivity of the Source
This file provides class for solver configuration.
Math library.
boost::shared_ptr< SolverConfiguration > LPSolverConfiguration
Definition: interfaces.h:25
@ SPECTRE_TYPE_LP
Definition: spectre.h:31
@ SPECTRE_TYPE_AUTRE
Definition: spectre.h:32
@ SPECTRE_TYPE_ABSO
Definition: spectre.h:29
OSpectreOctave Dz_top
OSpectreOctave Abar_left
OSpectreOctave Aatm
OSpectreOctave Agr_r
OSpectreOctave A
OSpectreOctave Abar
OSpectreOctave Abar_top
OSpectreOctave L
OSpectreOctave Agr_s
OSpectreOctave Dc
OSpectreOctave Abar_right
OSpectreOctave Adiv
OSpectreOctave Dz_right
TYTypeChemin pathType
OSpectreOctave LW
OSpectreOctave Agr_m
OSpectreOctave Dz_left
Data structure for intersections.
bool isInfra
Flag to define if is a infrastructure face.
bool bIntersect[2]
Flag to indicate the face cuts vertical plane ([0]) or horizontal plane ([1])
OSegment3D segInter[2]
tympan::AcousticMaterialBase * material
Pointer to a material.