BDSIM
BDSIM is a Geant4 extension toolkit for simulation of particle transport in accelerator beamlines.
BDSMagnetOuterFactoryCylindrical.cc
1/*
2Beam Delivery Simulation (BDSIM) Copyright (C) Royal Holloway,
3University of London 2001 - 2022.
4
5This file is part of BDSIM.
6
7BDSIM is free software: you can redistribute it and/or modify
8it under the terms of the GNU General Public License as published
9by the Free Software Foundation version 3 of the License.
10
11BDSIM is distributed in the hope that it will be useful, but
12WITHOUT ANY WARRANTY; without even the implied warranty of
13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14GNU General Public License for more details.
15
16You should have received a copy of the GNU General Public License
17along with BDSIM. If not, see <http://www.gnu.org/licenses/>.
18*/
19#include "BDSMagnetOuterFactoryCylindrical.hh"
20
21#include "BDSBeamPipe.hh"
22#include "BDSColours.hh"
23#include "BDSDebug.hh"
24#include "BDSExtent.hh"
25#include "BDSGeometryComponent.hh"
26#include "BDSGlobalConstants.hh"
27#include "BDSMagnetOuter.hh"
28#include "BDSMagnetOuterInfo.hh"
29#include "BDSMaterials.hh"
30#include "BDSSDType.hh"
31#include "BDSUtilities.hh" // for calculateorientation
32
33#include "globals.hh" // geant4 globals / types
34#include "G4CutTubs.hh"
35#include "G4LogicalVolume.hh"
36#include "G4Material.hh"
37#include "G4PVPlacement.hh"
38#include "G4SubtractionSolid.hh"
39#include "G4ThreeVector.hh"
40#include "G4Tubs.hh"
41#include "G4VisAttributes.hh"
42#include "G4VSolid.hh"
43
44#include <algorithm> // for std::max
45#include <cmath>
46#include <set>
47#include <utility> // for std::pair
48
49BDSMagnetOuterFactoryCylindrical::BDSMagnetOuterFactoryCylindrical():
50 magnetContainerRadius(0)
51{;}
52
54{
56 magnetContainerRadius = 0;
57}
58
60 G4double length,
61 const BDSBeamPipe* beamPipe,
62 G4double containerLength,
63 const BDSMagnetOuterInfo* recipe)
64{
65 G4double horizontalWidth = recipe->horizontalWidth;
66 G4double angleIn = recipe->angleIn;
67 G4double angleOut = recipe->angleOut;
68
69 // clear up variables
70 CleanUp();
71
72 // test input parameters - set global options as default if not specified
73 TestInputParameters(beamPipe,horizontalWidth);
74
75 // Simple cylinder if no poleface rotation, otherwise angled.
76 if (!BDS::IsFinite(angleIn) && !BDS::IsFinite(angleOut))
77 {
78 CreateCylindricalSolids(name,length, beamPipe, containerLength, horizontalWidth);
79 BuildMagnetContainerSolidStraight(name, containerLength, magnetContainerRadius);
80 }
81 else
82 {
83 std::pair<G4ThreeVector,G4ThreeVector> faces = BDS::CalculateFaces(angleIn,angleOut);
84 inputFaceNormal = faces.first;
85 outputFaceNormal = faces.second;
86
87 CreateCylindricalSolidsAngled(name, length, beamPipe, containerLength, horizontalWidth);
88 BuildMagnetContainerSolidAngled(name, containerLength, magnetContainerRadius);
89 }
90 return CommonFinalConstructor(name, length, recipe);
91}
92
94 G4double length,
95 const BDSBeamPipe* beamPipe,
96 G4double containerLength,
97 const BDSMagnetOuterInfo* recipe)
98{
99 G4double horizontalWidth = recipe->horizontalWidth;
100 G4double angleIn = recipe->angleIn;
101 G4double angleOut = recipe->angleOut;
102
103 //rectangular bends currently just make a shorter straight volume, so ignore angle for now
104 // clear up variables
105 CleanUp();
106
107 // test input parameters - set global options as default if not specified
108 TestInputParameters(beamPipe, horizontalWidth);
109
110 // Simple cylinder if no poleface rotation, otherwise angled.
111 if ((!BDS::IsFinite(angleIn)) && !BDS::IsFinite(angleOut))
112 {
113 CreateCylindricalSolids(name,length, beamPipe, containerLength, horizontalWidth);
114 BuildMagnetContainerSolidStraight(name,containerLength,0.5*horizontalWidth);
115 }
116 else
117 {
118 std::pair<G4ThreeVector,G4ThreeVector> faces = BDS::CalculateFaces(angleIn,angleOut);
119 inputFaceNormal = faces.first;
120 outputFaceNormal = faces.second;
121
122 CreateCylindricalSolidsAngled(name, length, beamPipe, containerLength, horizontalWidth);
123 BuildMagnetContainerSolidAngled(name, containerLength, 0.5*horizontalWidth);
124 }
125
126 return CommonFinalConstructor(name, length, recipe);
127}
128
130 G4double length,
131 BDSBeamPipe* beamPipe,
132 G4double containerLength,
133 const BDSMagnetOuterInfo* recipe)
134{
135 G4double horizontalWidth = recipe->horizontalWidth;
136 CleanUp();
137 CreateCylindricalSolids(name, length, beamPipe, containerLength, horizontalWidth);
138 return CommonFinalConstructor(name, length, recipe);
139}
140
142 G4double length,
143 BDSBeamPipe* beamPipe,
144 G4double containerLength,
145 const BDSMagnetOuterInfo* recipe)
146{
147 G4double horizontalWidth = recipe->horizontalWidth;
148 CleanUp();
149 CreateCylindricalSolids(name, length, beamPipe, containerLength, horizontalWidth);
150 return CommonFinalConstructor(name, length, recipe);
151}
152
154 G4double length,
155 BDSBeamPipe* beamPipe,
156 G4double containerLength,
157 const BDSMagnetOuterInfo* recipe)
158{
159 G4double horizontalWidth = recipe->horizontalWidth;
160 CleanUp();
161 CreateCylindricalSolids(name, length, beamPipe, containerLength, horizontalWidth);
162 return CommonFinalConstructor(name, length, recipe);
163}
164
166 G4double length,
167 BDSBeamPipe* beamPipe,
168 G4double containerLength,
169 const BDSMagnetOuterInfo* recipe)
170{
171 G4double horizontalWidth = recipe->horizontalWidth;
172 CleanUp();
173 CreateCylindricalSolids(name, length, beamPipe, containerLength, horizontalWidth);
174 return CommonFinalConstructor(name, length, recipe);
175}
176
178 G4double length,
179 BDSBeamPipe* beamPipe,
180 G4double containerLength,
181 const BDSMagnetOuterInfo* recipe)
182{
183 G4double horizontalWidth = recipe->horizontalWidth;
184 CleanUp();
185 CreateCylindricalSolids(name, length, beamPipe, containerLength, horizontalWidth);
186 auto result = CommonFinalConstructor(name, length, recipe);
187
188 // NOTE this is a reproduction of some calculations in component factory for now
189 G4double innerRadius = beamPipe->GetContainerRadius() + lengthSafetyLarge;
190 G4double outerRadius = horizontalWidth * 0.5;
191
192 if ( ((outerRadius - innerRadius)/outerRadius) < 0.05 )
193 {return result;} // mostly beam pipe with thin yoke - don't build more geometry
194
195 // build a current cylinder sheet but of the same material - purely to indicate to the user
196 // the geometry used for the yoke field calculation
197 G4double coilRadius = innerRadius + 0.25*(outerRadius - innerRadius);
198 G4double coilThickness = 0.05*coilRadius; // arbitrary 5%
199 G4VSolid* sheetSolid = new G4Tubs(name + "_sheet_solid",
200 coilRadius-coilThickness,
201 coilRadius+coilThickness,
202 length*0.5*0.8,
203 0, CLHEP::twopi);
204 G4LogicalVolume* sheetLV = new G4LogicalVolume(sheetSolid,
205 recipe->outerMaterial,
206 name + "_sheet_lv");
207 yokeLV = result->GetContainerLogicalVolume()->GetDaughter(0)->GetLogicalVolume();
208 auto sheetColour = BDSColours::Instance()->GetColour("copper");
209 auto sheetVis = new G4VisAttributes(*sheetColour);
210 sheetLV->SetVisAttributes(sheetVis);
211 auto pv = new G4PVPlacement(nullptr, // no rotation
212 G4ThreeVector(), // position
213 sheetLV, // lv to be placed
214 name + "_sheet_pv", // name
215 yokeLV, // mother lv to be placed in
216 false, // no boolean operation
217 0, // copy number
219 result->RegisterSolid(sheetSolid);
220 result->RegisterLogicalVolume(sheetLV);
221 result->RegisterPhysicalVolume(pv);
222 result->RegisterVisAttributes(sheetVis);
223 return result;
224}
225
227 G4double length,
228 BDSBeamPipe* beamPipe,
229 G4double containerLength,
230 const BDSMagnetOuterInfo* recipe)
231{
232 G4double horizontalWidth = recipe->horizontalWidth;
233 CleanUp();
234 CreateCylindricalSolids(name, length, beamPipe, containerLength, horizontalWidth);
235 return CommonFinalConstructor(name, length, recipe);
236}
237
239 G4double length,
240 BDSBeamPipe* beamPipe,
241 G4double containerLength,
242 const BDSMagnetOuterInfo* recipe)
243{
244 G4double horizontalWidth = recipe->horizontalWidth;
245 CleanUp();
246 CreateCylindricalSolids(name, length, beamPipe, containerLength, horizontalWidth);
247 return CommonFinalConstructor(name, length, recipe);
248}
249
251 G4double length,
252 BDSBeamPipe* beamPipe,
253 G4double containerLength,
254 const BDSMagnetOuterInfo* recipe)
255{
256 G4double horizontalWidth = recipe->horizontalWidth;
257 CleanUp();
258 CreateCylindricalSolids(name, length, beamPipe, containerLength, horizontalWidth);
259 return CommonFinalConstructor(name, length, recipe);
260}
261
263 G4double length,
264 const BDSBeamPipe* beamPipe,
265 G4double containerLength,
266 const BDSMagnetOuterInfo* recipe,
267 G4bool /*vertical*/)
268{
269 G4double horizontalWidth = recipe->horizontalWidth;
270 CleanUp();
271 // in this factory, h and v kickers will look the same so ignore bool vertical
272 // have to retain it though for virtual base class compatibility
273 CreateCylindricalSolids(name, length, beamPipe, containerLength, horizontalWidth);
274 return CommonFinalConstructor(name, length, recipe);}
275
277
279 G4double length,
280 const BDSBeamPipe* beamPipe,
281 G4double magnetContainerLength,
282 G4double horizontalWidth)
283{
284 // build the container for the whole magnet object - this horizontal width should be
285 // larger than the magnet outer piece width which is just 'horizontalWidth' wide.
286 magnetContainerRadius = (0.5 * horizontalWidth) + lengthSafetyLarge;
287 BuildMagnetContainerSolidStraight(name, magnetContainerLength, magnetContainerRadius);
288
289 if (beamPipe->ContainerIsCircular())
290 {
291 //circular beampipe so we can simply use its radius
292 yokeSolid = new G4Tubs(name + "_yoke_solid", // name
293 beamPipe->GetContainerRadius() + 2*lengthSafetyLarge, // inner radius
294 horizontalWidth*0.5, // outer radius
295 length*0.5-lengthSafety, // half length
296 0, // rotation start angle
297 CLHEP::twopi); // rotation finish angle
298
299 //container is similar but slightly wider and hollow (to allow placement of beampipe)
300 // inner radius of container is less than inner radius of yoke to contain it but still
301 // bigger than the outer radius of the beam pipe
302 containerSolid = new G4Tubs(name + "_container_solid", // name
303 beamPipe->GetContainerRadius() + lengthSafetyLarge, // inner radius
304 horizontalWidth*0.5 + lengthSafetyLarge,// outer radius
305 length*0.5, // half length
306 0, // rotation start angle
307 CLHEP::twopi); // rotation finish angle
308 }
309 else
310 {
311 G4VSolid* yokeSolidCylinder = new G4Tubs(name + "_yoke_solid_cylinder",// name
312 0, // inner radius - for unambiguous subtraction
313 horizontalWidth*0.5, // outer radius
314 length*0.5-lengthSafety, // half length
315 0, // rotation start angle
316 CLHEP::twopi); // rotation finish angle
317 allSolids.insert(yokeSolidCylinder);
318 yokeSolid = new G4SubtractionSolid(name + "_yoke_solid",
319 yokeSolidCylinder,
320 beamPipe->GetContainerSubtractionSolid());
321
322 //container is similar but slightly wider
323 G4VSolid* containerSolidCylinder = new G4Tubs(name + "_container_solid_cylinder",// name
324 0, // inner radius - for unambiguous subtraction
325 horizontalWidth*0.5 + lengthSafetyLarge, // outer radius
326 length*0.5, // half length
327 0, // rotation start angle
328 CLHEP::twopi); // rotation finish angle
329 allSolids.insert(containerSolidCylinder);
330 containerSolid = new G4SubtractionSolid(name + "_container_solid",
331 containerSolidCylinder,
332 beamPipe->GetContainerSubtractionSolid());
333 }
334 allSolids.insert(yokeSolid);
335}
336
337// Function for cylinder with angled faces - for pole face rotation in dipoles.
338void BDSMagnetOuterFactoryCylindrical::CreateCylindricalSolidsAngled(G4String name,
339 G4double length,
340 const BDSBeamPipe* beamPipe,
341 G4double magnetContainerLength,
342 G4double horizontalWidth)
343{
344 // build the container for the whole magnet object - this horizontal width should be
345 // larger than the magnet outer piece width which is just 'horizontalWidth' wide.
346 magnetContainerRadius = (0.5 * horizontalWidth) + lengthSafety;
347 BuildMagnetContainerSolidStraight(name, magnetContainerLength, magnetContainerRadius);
348
349 if (beamPipe->ContainerIsCircular())
350 {
351 //circular beampipe so we can simply use its radius
352 yokeSolid = new G4CutTubs(name + "_yoke_solid", // name
353 beamPipe->GetContainerRadius() + 2*lengthSafety, // inner radius
354 horizontalWidth*0.5, // outer radius
355 length*0.5-lengthSafety, // half length
356 0, // rotation start angle
357 CLHEP::twopi, // rotation finish angle
358 inputFaceNormal, // input face normal
359 outputFaceNormal); // output face normal);
360
361 //container is similar but slightly wider and hollow (to allow placement of beampipe)
362 containerSolid = new G4CutTubs(name + "_container_solid", // name
363 beamPipe->GetContainerRadius() + lengthSafety, // inner radius
364 horizontalWidth*0.5 + lengthSafety,// outer radius
365 length*0.5, // half length
366 0, // rotation start angle
367 CLHEP::twopi, // rotation finish angle
368 inputFaceNormal, // input face normal
369 outputFaceNormal); // output face normal);
370 }
371 else
372 {
373 G4VSolid* yokeSolidCylinder = new G4CutTubs(name + "_yoke_solid_cylinder",// name
374 0, // inner radius - for unambiguous subtraction
375 horizontalWidth*0.5, // outer radius
376 length*0.5-lengthSafety,// half length
377 0, // rotation start angle
378 CLHEP::twopi, // rotation finish angle
379 inputFaceNormal, // input face normal
380 outputFaceNormal); // output face normal);
381 allSolids.insert(yokeSolidCylinder);
382 yokeSolid = new G4SubtractionSolid(name + "_yoke_solid",
383 yokeSolidCylinder,
384 beamPipe->GetContainerSubtractionSolid());
385
386 //container is similar but slightly wider
387 G4VSolid* containerSolidCylinder = new G4CutTubs(name + "_container_solid_cylinder",// name
388 0, // inner radius - for unambiguous subtraction
389 horizontalWidth*0.5 + lengthSafety,// outer radius
390 length*0.5, // half length
391 0, // rotation start angle
392 CLHEP::twopi, // rotation finish angle
393 inputFaceNormal, // input face normal
394 outputFaceNormal);// output face normal);
395 allSolids.insert(containerSolidCylinder);
396 containerSolid = new G4SubtractionSolid(name + "_container_solid",
397 containerSolidCylinder,
398 beamPipe->GetContainerSubtractionSolid());
399 }
400 allSolids.insert(yokeSolid);
401}
402
404 G4double& horizontalWidth)
405{
406 // ensure box size is bigger than the beampipe
407 if (beamPipe->ContainerIsCircular())
408 {
409 // if it's circular, just check radius
410 if (horizontalWidth < 2*(beamPipe->GetContainerRadius()) )
411 {horizontalWidth = 2*(beamPipe->GetContainerRadius()) + 1*CLHEP::mm;}
412 }
413 else
414 {
415 // it's not circular - have a look at extents
416 // +ve - -ve
417 G4double extentX = beamPipe->GetExtent().DX();
418 G4double extentY = beamPipe->GetExtent().DY();
419 if ( (horizontalWidth < extentX) || (horizontalWidth < extentY) )
420 {
421 // horizontalWidth isn't sufficient for range in x or y
422 horizontalWidth = std::max(extentX,extentY) + 1*CLHEP::mm;
423 }
424 }
425}
426
428 G4double length,
429 const BDSMagnetOuterInfo* recipe)
430{
431 G4Material* outerMaterial = recipe->outerMaterial;
432 if (!outerMaterial)
433 {outerMaterial = BDSMaterials::Instance()->GetMaterial(BDSGlobalConstants::Instance()->EmptyMaterial());}
434 G4Colour* colour = recipe->colour;
435
436 CreateLogicalVolumes(name, colour, outerMaterial);
438 CreateMagnetContainerComponent();
439
440 // PLACEMENT
441 // place the components inside the container
442 // note we don't need the pointer for anything - it's registered upon construction with g4
443 yokePV = new G4PVPlacement(nullptr, // no rotation
444 G4ThreeVector(), // position
445 yokeLV, // lv to be placed
446 name + "_yoke_pv", // name
447 containerLV, // mother lv to be placed in
448 false, // no boolean operation
449 0, // copy number
450 checkOverlaps); // whether to check overlaps
451
452 // record extents
453 // container radius is the same for all methods as all cylindrical
454 BDSExtent ext = BDSExtent(magnetContainerRadius, magnetContainerRadius, length*0.5);
455
456 // build the BDSMagnetOuter instance and return it
457 BDSMagnetOuter* outer = new BDSMagnetOuter(containerSolid,
458 containerLV, ext,
459 magnetContainer);
460
461 SetFaceNormals(outer);
462
463 // register all objects that go with the final geometry component (from internal sets)
464 outer->RegisterSolid(allSolids);
465 outer->RegisterLogicalVolume(yokeLV);
466 if (sensitiveOuter)
467 {outer->RegisterSensitiveVolume(yokeLV, BDSSDType::energydep);}
468 outer->RegisterPhysicalVolume(yokePV);
469 outer->RegisterVisAttributes(outerVisAttributes);
470 // no rotation matrices used in this factory
471 return outer;
472}
473
474
A holder class for a piece of beam pipe geometry.
Definition: BDSBeamPipe.hh:45
G4VSolid * GetContainerSubtractionSolid() const
default destructor sufficient as G4 manages solids and LVs
Definition: BDSBeamPipe.hh:63
G4double GetContainerRadius() const
If it is circular, we need the radius.
Definition: BDSBeamPipe.hh:70
G4bool ContainerIsCircular() const
Definition: BDSBeamPipe.hh:68
static BDSColours * Instance()
singleton pattern
Definition: BDSColours.cc:33
G4Colour * GetColour(const G4String &type, G4bool normaliseTo255=true)
Get colour from name.
Definition: BDSColours.cc:202
Holder for +- extents in 3 dimensions.
Definition: BDSExtent.hh:39
G4double DX() const
The difference in a dimension.
Definition: BDSExtent.hh:83
G4double DY() const
The difference in a dimension.
Definition: BDSExtent.hh:84
G4bool checkOverlaps
Cache of global constants variable.
G4double lengthSafety
Cache of global constants variable.
G4double lengthSafetyLarge
Cache of global constants variable.
BDSExtent GetExtent() const
Accessor - see member for more info.
void RegisterLogicalVolume(G4LogicalVolume *logicalVolume)
void RegisterPhysicalVolume(G4VPhysicalVolume *physicalVolume)
void RegisterVisAttributes(G4VisAttributes *visAttribute)
void RegisterSolid(G4VSolid *solid)
void RegisterSensitiveVolume(G4LogicalVolume *sensitiveVolume, BDSSDType sensitivityType)
static BDSGlobalConstants * Instance()
Access method.
virtual void CleanUp()
Empty containers for next use - factories are never deleted so can't rely on scope.
virtual void CreateLogicalVolumes(const G4String &name, G4Colour *colour, G4Material *outerMaterial)
void SetFaceNormals(BDSMagnetOuter *outer)
Copy face normals from members to an instance of outer.
virtual void SetUserLimits()
Attach default user limits to all logical volumes.
G4bool sensitiveOuter
Cache of global constants variable.
G4VSolid * yokeSolid
Solid for outer part that connects all poles.
void BuildMagnetContainerSolidStraight(const G4String &name, G4double magnetContainerLength, G4double magnetContainerRadius)
void BuildMagnetContainerSolidAngled(const G4String &name, G4double magnetContainerLength, G4double magnetContainerRadius, G4bool flatFaces=false)
virtual BDSMagnetOuter * CreateKicker(G4String name, G4double length, const BDSBeamPipe *beamPipe, G4double containerLength, const BDSMagnetOuterInfo *recipe, G4bool vertical)
horizontal and vertical kicker outer volume
virtual BDSMagnetOuter * CreateMuonSpoiler(G4String name, G4double length, BDSBeamPipe *beamPipe, G4double containerLength, const BDSMagnetOuterInfo *recipe)
muon spoiler outer volume
virtual BDSMagnetOuter * CreateSextupole(G4String name, G4double length, BDSBeamPipe *beamPipe, G4double containerLength, const BDSMagnetOuterInfo *recipe)
sextupole outer volume
void CreateCylindricalSolids(G4String name, G4double length, const BDSBeamPipe *beamPipe, G4double magnetContainerLength, G4double boxSize)
Only really one function needed for this factory private to this factory only.
virtual BDSMagnetOuter * CreateRectangularBend(G4String name, G4double length, const BDSBeamPipe *beamPipe, G4double containerLength, const BDSMagnetOuterInfo *recipe)
rectangular bend outer volume
virtual BDSMagnetOuter * CreateMultipole(G4String name, G4double length, BDSBeamPipe *beamPipe, G4double containerLength, const BDSMagnetOuterInfo *recipe)
general multipole outer volume - could be any 2N order multipole
virtual BDSMagnetOuter * CreateSectorBend(G4String name, G4double length, const BDSBeamPipe *beamPipe, G4double containerLength, const BDSMagnetOuterInfo *recipe)
sector bend outer volume
virtual BDSMagnetOuter * CreateDecapole(G4String name, G4double length, BDSBeamPipe *beamPipe, G4double containerLength, const BDSMagnetOuterInfo *recipe)
decapole outer volume
BDSMagnetOuter * CommonFinalConstructor(G4String name, G4double length, const BDSMagnetOuterInfo *recipe)
virtual BDSMagnetOuter * CreateSolenoid(G4String name, G4double length, BDSBeamPipe *beamPipe, G4double containerLength, const BDSMagnetOuterInfo *recipe)
solenoid outer volume
virtual BDSMagnetOuter * CreateQuadrupole(G4String name, G4double length, BDSBeamPipe *beamPipe, G4double containerLength, const BDSMagnetOuterInfo *recipe)
quadrupole outer volume
virtual BDSMagnetOuter * CreateOctupole(G4String name, G4double length, BDSBeamPipe *beamPipe, G4double containerLength, const BDSMagnetOuterInfo *recipe)
octupole outer volume
virtual BDSMagnetOuter * CreateRfCavity(G4String name, G4double length, BDSBeamPipe *beamPipe, G4double containerLength, const BDSMagnetOuterInfo *recipe)
RF cavity outer volume.
virtual void CleanUp()
Empty containers for next use - factories are never deleted so can't rely on scope.
void TestInputParameters(const BDSBeamPipe *beamPipe, G4double &horizontalWidthIn)
test inputs for no null pointers or overlapping volumes due to poorly defined sizes
Holder struct of all information required to create the outer geometry of a magnet.
An object for both the returned magnet outer body but also a tight fitting container for the whole ma...
static BDSMaterials * Instance()
Singleton pattern access.
Definition: BDSMaterials.cc:38
G4Material * GetMaterial(G4String material) const
Get material by name.
G4bool IsFinite(G4double value, G4double tolerance=std::numeric_limits< double >::epsilon())
std::pair< G4ThreeVector, G4ThreeVector > CalculateFaces(G4double angleInIn, G4double angleOutIn)
Calculate input and output normal vector.