Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/etalon cavity #323

Open
wants to merge 5 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 36 additions & 3 deletions Source/Fields/LMCCylindricalCavity.cc
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@ namespace locust
fProbeGain( {1., 1., 1.}),
fCavityProbeZ( {0., 0., 0.} ),
fCavityProbeRFrac( {0.5, 0.5, 0.5} ),
fCavityProbeTheta( {0.0, 0.0, 0.0} )
fCavityProbeTheta( {0.0, 0.0, 0.0} ),
fCaterpillarCavity( false ),
fApplyDopplerShift( true )
{}

CylindricalCavity::~CylindricalCavity() {}
Expand All @@ -33,6 +35,16 @@ namespace locust
return false;
}

if( aParam.has( "caterpillar-cavity" ) )
{
fCaterpillarCavity = aParam["caterpillar-cavity"]().as_bool();
}

if( aParam.has( "apply-doppler-shift" ) )
{
fApplyDopplerShift = aParam["apply-doppler-shift"]().as_bool();
}

if( aParam.has( "cavity-radius" ) )
{
SetDimR( aParam["cavity-radius"]().as_double() );
Expand Down Expand Up @@ -227,11 +239,16 @@ namespace locust
double vz = tKassParticleXP[5];
double term1 = fFieldCore->GetBesselNKPrimeZeros(l,m) / GetDimR();
double term2 = n * LMCConst::Pi() / GetDimL();
if ( fCaterpillarCavity )
{
// Assume n-channels is the same as the number of etalon sections:
term2 *= GetNChannels();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This implementation should work consistently internal to this class, but may run into other issues when called with the CavitySignalGenerator class. See further comments around (new) line 343.

}
double lambda = 1. / pow( 1. / 4. / LMCConst::Pi() / LMCConst::Pi() * ( term1*term1 + term2*term2 ), 0.5);
double lambda_c = 2 * LMCConst::Pi() * GetDimR() / fFieldCore->GetBesselNKPrimeZeros(l,m);
double vp = LMCConst::C() / pow( 1. - lambda*lambda/lambda_c/lambda_c, 0.5 );
double dopplerShift = 0.;
if (vp > 0.) dopplerShift = vz / vp;
if ((vp > 0.) && (fApplyDopplerShift)) dopplerShift = vz / vp;
freqPrime.push_back( ( 1. + dopplerShift ) * tKassParticleXP[7] );
return freqPrime;
}
Expand Down Expand Up @@ -325,7 +342,23 @@ namespace locust
std::vector<double> tEFieldAtProbe;
for (unsigned index=0; index<GetNChannels(); index++)
{
tEFieldAtProbe.push_back( NormalizedEFieldMag(GetNormalizedModeField(l,m,n,tProbeLocation[index],0,teMode)) );
if ( !fCaterpillarCavity )
{
tEFieldAtProbe.push_back( NormalizedEFieldMag(GetNormalizedModeField(l,m,n,tProbeLocation[index],0,teMode)) );
}
else
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm a little worried about the current implementation. First, I do believe it's internally consistent. Because the probes are all in the longitudinal center of their respective subcavities, and because we're always normalizing the modes anyway, there's no numerical difference between what you're doing and just setting up three separate ideal modes. One issue, though, would be if you move the probes off center (e.g. z=0.1 in some systematic study), where the L/D ratio in your doppler calculation would be different by a factor of 3 from this EfieldAtProbe part. Thus displacing your probe from the center could have an improperly scaled effect (e.g. partial effect from z=0.03 or z=0.3 rather than z=0.1, depending on how you define your displacement).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the read. For a test with a probe at e.g. z=0.1, should the Doppler shift (as implemented) be affected? In general, it looks like we have not been doing that. Or is there a different interpretation?

Copy link
Contributor

@jkgaison65 jkgaison65 Nov 12, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's take an example where the total cavity length is 12m, with each segment being 4m long. The doppler frequency is properly getting adjusted to use L=4m in the linked reference. But since tProbeLocation stores the z-coordinate in real units, and GetNormalizedModeField works in the units of the full cavity (L=12m in this case), setting tProbeLocation = (r,theta,z=0.1) would effectively only be using tProbeLocation = (r, theta, z=0.033) if you're interpreting it as being the position in the segment rather than the whole cavity.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

aha, thanks. so I was thinking that the probe z-locations should be defined in terms of the whole cavity length, as in e.g.
"cavity-length": 4.9,
"caterpillar-cavity": true,
"cavity-probe-z0": 0.817,
"cavity-probe-z1": 2.450,
"cavity-probe-z2": 4.084,

and that GetNormalizedModeField() should return the field that was normalized over the whole cavity volume (a.k.a. over the entire imported mode map, as usual). But, even assuming that all of that happens, then there is still a complication in the Doppler shift. So the present approach is to consider each etalon segment as a standalone cavity when implementing the Doppler shift, independent of the overall field normalization.

{
// Assume 1 channel per etalon section:
int indexSubCavity = (int)(( tKassParticleXP[2] + GetDimL()/2. ) * GetNChannels() / GetDimL());
if ( indexSubCavity == index ) // If the electron is in the etalon section where the probe is:
{
tEFieldAtProbe.push_back( NormalizedEFieldMag(GetNormalizedModeField(l,m,n,tProbeLocation[index],0,teMode)) );
}
else
{
tEFieldAtProbe.push_back( 0. );
}
}
}

return {fProbeGain[0] * tEFieldAtProbe[0], fProbeGain[1] * tEFieldAtProbe[1], fProbeGain[2] * tEFieldAtProbe[2]};
Expand Down
2 changes: 2 additions & 0 deletions Source/Fields/LMCCylindricalCavity.hh
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,8 @@ namespace locust
std::vector<double> fCavityProbeRFrac;
std::vector<double> fCavityProbeTheta;
std::vector<double> fProbeGain;
bool fCaterpillarCavity;
bool fApplyDopplerShift;

};

Expand Down
3 changes: 2 additions & 1 deletion Source/Fields/LMCField.cc
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,9 @@ namespace locust
fCentralFrequency(1.63e11),
fAvgDotProductFactor( 0. ),
fNModes( 2 ),
fNChannels( 1 ),
fbMultiMode( false ),
fTM111( false ),
fTM111( false ),
fR( 0.18 ),
fL( 3.0 ),
fX( 0.010668 ),
Expand Down
53 changes: 27 additions & 26 deletions Source/Fields/LMCModeMapCavity.cc
Original file line number Diff line number Diff line change
Expand Up @@ -116,27 +116,27 @@ namespace locust
std::string token;
std::stringstream ss(lineContent);
int wordCount = 0;
double r, theta, z;
double r, theta, z;
int i,j,k;
double Erho,Etheta;
double Ex, Ey, Ez;
while (ss >> token)
{
if (wordCount == 0)
{
r = std::stod(token);
r = std::stod(token);
i = (int)((r-fDim1_min)/(fDim1_max-fDim1_min)*(fnPixel1)); // var1 position
}
}
else if (wordCount == 1)
{
theta = std::stod(token);
j = (int)((theta-fDim2_min)/(fDim2_max-fDim2_min)*(fnPixel2)); // var2 position
}
{
theta = std::stod(token);
j = (int)((theta-fDim2_min)/(fDim2_max-fDim2_min)*(fnPixel2)); // var2 position
}
else if (wordCount == 2)
{
z = std::stod(token);
k = (int)((z-fDim3_min)/(fDim3_max-fDim3_min)*(fnPixel3)); // var3 position
}
{
z = std::stod(token);
k = (int)((z-fDim3_min)/(fDim3_max-fDim3_min)*(fnPixel3)); // var3 position
}
else if (wordCount == 3) Ex = std::stod(token); // mode E field value
else if (wordCount == 4) Ey = std::stod(token); // mode E field value
else if (wordCount == 5) Ez = std::stod(token); // mode E field value
Expand All @@ -148,37 +148,38 @@ namespace locust
++wordCount;
}

if ((i==fnPixel1) or (j==fnPixel2) or (k==fnPixel3))
{
continue;
}
if ((i==fnPixel1) or (j==fnPixel2) or (k==fnPixel3))
{
continue;
}
if ((i>=fnPixel1) or (j>=fnPixel2) or (k>=fnPixel3))
{
LERROR(lmclog,"Imported mode map dimensions don't agree with those in \"" << aFilename <<".\" Double check dim[1,2,3]-max.");
return false;
}

//Must convert E field from cartesian coordinates to cylindrical coordinates
if(r<1.e-10)
{
Erho = 0.;
Etheta = 0.;
}
else
{
if(r<1.e-10)
{
Erho = 0.;
Etheta = 0.;
}
else
{
Erho = ((Ex * r*cos(theta)) + Ey * r*sin(theta)) / r;
Etheta = ((Ey * r*cos(theta)) - Ex * r*sin(theta)) / r;
}
Etheta = ((Ey * r*cos(theta)) - Ex * r*sin(theta)) / r;
}

std::vector E_input = {Erho,Etheta,Ez};
fModeMapTE_E[i][j][k] = E_input;
// printf("read var1 is %g, var2 is %g, E is %g\n", fModeMapTE_E.back()[0], fModeMapTE_E.back()[1], fModeMapTE_E.back()[2]);
//printf("read var1 is %g, var2 is %g, E is %g\n", fModeMapTE_E.back()[0], fModeMapTE_E.back()[1], fModeMapTE_E.back()[2]);

}
}

modeMapFile.close();

//Reset dimensions from import file to actual cavity dimensions in case they don't match up
//Reset dimensions from import file to actual cavity dimensions in case they don't match up
MatchCavityDimensions(aParam);

return true;
Expand Down
Loading