1 //                                               -*- C++ -*-
2 /**
3  *  @brief Approximation algorithm for system events based on FORM
4  *
5  *  Copyright 2005-2021 Airbus-EDF-IMACS-ONERA-Phimeca
6  *
7  *  This library is free software: you can redistribute it and/or modify
8  *  it under the terms of the GNU Lesser General Public License as published by
9  *  the Free Software Foundation, either version 3 of the License, or
10  *  (at your option) any later version.
11  *
12  *  This library is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *  GNU Lesser General Public License for more details.
16  *
17  *  You should have received a copy of the GNU Lesser General Public License
18  *  along with this library.  If not, see <http://www.gnu.org/licenses/>.
19  *
20  */
21 
22 #include "openturns/SystemFORM.hxx"
23 #include "openturns/PersistentObjectFactory.hxx"
24 #include "openturns/FORM.hxx"
25 #include "openturns/IntersectionEvent.hxx"
26 #include "openturns/UnionEvent.hxx"
27 #include "openturns/Normal.hxx"
28 
29 BEGIN_NAMESPACE_OPENTURNS
30 
31 CLASSNAMEINIT(SystemFORM);
32 
33 static Factory<SystemFORM> Factory_SystemFORM;
34 
SystemFORM()35 SystemFORM::SystemFORM()
36   : Analytical()
37 {
38   // Nothing to do
39 }
40 
SystemFORM(const OptimizationAlgorithm & nearestPointAlgorithm,const RandomVector & event,const Point & physicalStartingPoint)41 SystemFORM::SystemFORM(const OptimizationAlgorithm & nearestPointAlgorithm,
42                        const RandomVector & event,
43                        const Point & physicalStartingPoint)
44   : Analytical()
45 {
46   setNearestPointAlgorithm(nearestPointAlgorithm);
47   setPhysicalStartingPoint(physicalStartingPoint);
48   setEvent(event);
49 }
50 
51 /* Virtual constructor */
clone() const52 SystemFORM * SystemFORM::clone() const
53 {
54   return new SystemFORM(*this);
55 }
56 
57 /* Result accessor */
getResult() const58 MultiFORMResult SystemFORM::getResult() const
59 {
60   return multiFORMResult_;
61 }
62 
63 
64 /* String converter */
__repr__() const65 String SystemFORM::__repr__() const
66 {
67   OSS oss;
68   oss << "class=" << SystemFORM::GetClassName()
69       << " " << Analytical::__repr__()
70       << " result=" << multiFORMResult_;
71   return oss;
72 }
73 
setEvent(const RandomVector & event)74 void SystemFORM::setEvent(const RandomVector & event)
75 {
76   // check that the event is in disjunctive normal form (union of intersections)
77   const UnionEvent *unionEvent = dynamic_cast<UnionEvent*>(event.getImplementation().get());
78   Collection<RandomVector> unionCollection;
79   if (unionEvent)
80     unionCollection = unionEvent->getEventCollection();
81   else // just a single intersection
82     unionCollection.add(event);
83   for (UnsignedInteger i = 0; i < unionCollection.getSize(); ++ i)
84   {
85     if (unionCollection[i].getImplementation()->getClassName() == "IntersectionEvent")
86     {
87       const IntersectionEvent *intersectionEvent = dynamic_cast<IntersectionEvent*>(unionCollection[i].getImplementation().get());
88       Collection<RandomVector> intersectionCollection(intersectionEvent->getEventCollection());
89       for (UnsignedInteger j = 0; j < intersectionCollection.getSize(); ++ j)
90         if (intersectionCollection[j].getImplementation()->getClassName() != "ThresholdEventImplementation")
91           throw InvalidArgumentException(HERE) << "Event is not in disjunctive normal form";
92     }
93     else if (unionCollection[i].getImplementation()->getClassName() != "ThresholdEventImplementation")
94       throw InvalidArgumentException(HERE) << "Event is not in disjunctive normal form";
95   }
96   Analytical::setEvent(event);
97 }
98 
99 /* Run */
run()100 void SystemFORM::run()
101 {
102   // Collect the flat list of leaf events from the DNF event and the the lists of ids for each parallel region
103   Collection<RandomVector> leafEventCollection;
104   const UnionEvent *unionEvent = dynamic_cast<UnionEvent*>(getEvent().getImplementation().get());
105   Collection<RandomVector> unionCollection;
106   if (unionEvent)
107     unionCollection = unionEvent->getEventCollection();
108   else // just a single intersection
109     unionCollection.add(getEvent());
110   Collection<Indices> parallelRegionIdCollection;
111   for (UnsignedInteger i = 0; i < unionCollection.getSize(); ++ i)
112   {
113     if (unionCollection[i].getImplementation()->getClassName() == "IntersectionEvent")
114     {
115       const IntersectionEvent *intersectionEvent = dynamic_cast<IntersectionEvent*>(unionCollection[i].getImplementation().get());
116       Collection<RandomVector> intersectionCollection(intersectionEvent->getEventCollection());
117       leafEventCollection.add(intersectionCollection);
118       Indices parallelRegionId(intersectionCollection.getSize());
119       for (UnsignedInteger j = 0; j < intersectionCollection.getSize(); ++ j)
120       {
121         parallelRegionId[j] = intersectionCollection[j].getId();
122       }
123       parallelRegionIdCollection.add(parallelRegionId);
124     }
125     else
126     {
127       // single event in parallel region
128       leafEventCollection.add(unionCollection[i]);
129       parallelRegionIdCollection.add(Indices(1, unionCollection[i].getId()));
130     }
131   }
132 
133   // for each leaf events perform FORM
134   std::map<UnsignedInteger, Scalar> idToBetaMap;
135   std::map<UnsignedInteger, Point> idToAlphaMap;
136   Collection<FORMResult> formResultCollection;
137   for (UnsignedInteger i = 0; i < leafEventCollection.getSize(); ++ i)
138   {
139     const UnsignedInteger id = leafEventCollection[i].getId();
140     if (idToBetaMap.find(id) == idToBetaMap.end()) // if not already computed for this event
141     {
142       FORM algo(getNearestPointAlgorithm(), leafEventCollection[i], getPhysicalStartingPoint());
143       algo.run();
144 
145       const FORMResult result(algo.getResult());
146       const Scalar beta = result.getGeneralisedReliabilityIndex();
147       idToBetaMap[id] = beta;
148       idToAlphaMap[id] = result.getStandardSpaceDesignPoint() * (1.0 / beta);
149       formResultCollection.add(result);
150       LOGINFO(OSS() << "SystemFORM: event=" << id << " beta=" << result.getGeneralisedReliabilityIndex());
151     }
152   }
153 
154   // generate all poincarre terms
155   Collection<Indices> poincarreRegion;
156   UnsignedInteger numberOfPoincarreTerms = 0;
157   for (UnsignedInteger k = 0; k < unionCollection.getSize(); ++ k)
158   {
159     // retrieve region
160     const Indices region(parallelRegionIdCollection[k]);
161 
162     // recursive expansion of the Poincarre formula
163     for (UnsignedInteger i = 0; i < numberOfPoincarreTerms; ++ i)
164     {
165       // intersection of region / poincarreRegion[i]
166       Indices intersection(region);
167       const UnsignedInteger poincarreRegionSize = poincarreRegion[i].getSize();
168       for (UnsignedInteger j = 0; j < poincarreRegionSize; ++ j)
169         if (!region.contains(poincarreRegion[i][j]))
170           intersection.add(poincarreRegion[i][j]);
171       poincarreRegion.add(intersection);
172     }
173     poincarreRegion.add(region);
174 
175     // update number of poincarre terms (=2^n-1)
176     numberOfPoincarreTerms = 2 * numberOfPoincarreTerms + 1;
177   }
178 
179   Scalar eventProbability = 0.0;
180   for (UnsignedInteger k = 0; k < numberOfPoincarreTerms; ++ k)
181   {
182     // retrieve region
183     const Indices region(poincarreRegion[k]);
184     const UnsignedInteger regionSize = region.getSize();
185 
186     // set region beta
187     Point regionBeta(regionSize);
188     for (UnsignedInteger i = 0; i < regionSize; ++ i)
189       regionBeta[i] = idToBetaMap[region[i]];
190 
191     // set the correlation matrix C
192     CovarianceMatrix C(regionSize);
193     for (UnsignedInteger i = 0; i < regionSize; ++ i)
194       for (UnsignedInteger j = 0; j < i; ++ j)
195         C(i, j) = idToAlphaMap[region[i]].dot(idToAlphaMap[region[j]]);
196 
197     // Regularize C
198     Scalar cumulatedScaling = 0.0;
199     const Scalar startingScaling = ResourceMap::GetAsScalar("SystemFORM-StartingScaling");
200     const Scalar maximalScaling = ResourceMap::GetAsScalar("SystemFORM-MaximalScaling");
201     Scalar scaling = startingScaling;
202     while (!C.isPositiveDefinite())
203     {
204       cumulatedScaling += scaling;
205       for (UnsignedInteger index = 0; index < regionSize; ++index)
206         C(index, index) += scaling;
207       scaling *= 2.0;
208 
209       // No reasonable regularization succeeded
210       if (cumulatedScaling >= maximalScaling)
211         throw InvalidArgumentException(HERE) << "Could not regularize, scaling up to " << cumulatedScaling << " was not enough";
212     }
213 
214     // compute the parallel region probability
215     const Point mu(regionSize, 0.0);
216     const Normal normal(mu, C);
217     const Scalar poincarreProbability = normal.computeCDF(-1.0 * regionBeta);
218 
219     LOGINFO(OSS() << "SystemFORM: poincarre probability [" << k << "]=" << poincarreProbability);
220 
221     // trick to update the poincarre sum
222     // we invert the sign of each consecutive term to follow the order of our recursive expansion of the Poincarre formula
223     eventProbability = -eventProbability + poincarreProbability;
224   }
225 
226   // store results
227   multiFORMResult_ = MultiFORMResult(formResultCollection);
228   multiFORMResult_.setEventProbability(eventProbability);
229 }
230 
231 
232 /* Method save() stores the object through the StorageManager */
save(Advocate & adv) const233 void SystemFORM::save(Advocate & adv) const
234 {
235   Analytical::save(adv);
236   adv.saveAttribute("multiFORMResult_", multiFORMResult_);
237 }
238 
239 
240 /* Method load() reloads the object from the StorageManager */
load(Advocate & adv)241 void SystemFORM::load(Advocate & adv)
242 {
243   Analytical::load(adv);
244   adv.loadAttribute("multiFORMResult_", multiFORMResult_);
245 }
246 
247 END_NAMESPACE_OPENTURNS
248