diff --git a/sirepo/package_data/static/html/silas-thermal-transport.html b/sirepo/package_data/static/html/silas-thermal-transport.html
index ff5e7fdd8d..28778ae096 100644
--- a/sirepo/package_data/static/html/silas-thermal-transport.html
+++ b/sirepo/package_data/static/html/silas-thermal-transport.html
@@ -18,5 +18,8 @@
@@ -50,7 +46,7 @@ SIREPO.app.factory('silasService', function(appState) {
const self = {};
self.computeModel = (analysisModel) => {
- if (['crystalAnimation', 'crystal3dAnimation', 'plotAnimation', 'plot2Animation', 'tempHeatMapAnimation', 'tempProfileAnimation'].indexOf(analysisModel) >= 0) {
+ if (['crystalAnimation', 'crystal3dAnimation', 'tempHeatMapAnimation', 'tempProfileAnimation'].indexOf(analysisModel) >= 0) {
return 'crystalAnimation';
}
if (['laserPulseAnimation', 'laserPulse2Animation'].includes(analysisModel)) {
@@ -189,14 +185,6 @@ SIREPO.app.controller('CrystalController', function (appState, frameCache, persi
self.simState = persistentSimulation.initSimulationState(self);
self.simState.errorMessage = () => errorMessage;
-
- appState.whenModelsLoaded($scope, () => {
- $scope.$on('plotAnimation.summaryData', function (e, data) {
- if (data.crystalLength && data.crystalLength != silasService.getThermalCrystal().length) {
- frameCache.setFrameCount(0);
- }
- });
- });
});
SIREPO.app.directive('appFooter', function(appState, silasService) {
@@ -677,11 +665,12 @@ SIREPO.app.directive('crystal3d', function(appState, plotting, silasService, plo
let len = [];
let indices = [];
let verts = data.vertices;
+ const c = appState.applicationState().thermalTransportCrystal.crystal;
let size = $scope.boundAxis == 'Z'
- ? silasService.getThermalCrystal().length
- : appState.applicationState().thermalTransportCrystal.crystal.inversion_mesh_extent * 2;
+ ? c.length
+ : c.inversion_mesh_extent * 2 * 100;
let axisIdx = $scope.axes.indexOf($scope.boundAxis);
- $scope.bound = (size + 0.01) * ($scope.sliderValue - 50) / 100;
+ $scope.bound = (size + 0.01) * $scope.sliderValue / 100 - size / 2;
for (let i = 0; i < data.indices.length; i += 3) {
if (checkBounds(i, axisIdx)) {
indices.push(data.indices[i], data.indices[i + 1], data.indices[i + 2]);
@@ -732,7 +721,6 @@ SIREPO.app.directive('crystal3d', function(appState, plotting, silasService, plo
camera.setFocalPoint(0, 0, 0);
camera.setViewUp(0, -1, 0);
renderer.resetCamera();
- camera.zoom(1.3);
orientationMarker.updateMarkerOrientation();
}
fsRenderer.getRenderWindow().render();
diff --git a/sirepo/package_data/static/json/silas-schema.json b/sirepo/package_data/static/json/silas-schema.json
index 1bc74212e9..fc4d644715 100644
--- a/sirepo/package_data/static/json/silas-schema.json
+++ b/sirepo/package_data/static/json/silas-schema.json
@@ -149,7 +149,7 @@
"x": ["Horizontal Value to Plot", "ValueList", "x"],
"y1": ["Vertical Value to Plot", "ValueList", "y"],
"histogramBins": ["Histogram Bins", "Integer", 200],
- "colorMap": ["Color Map", "ColorMap", "afmhot"]
+ "colorMap": ["Color Map", "ColorMap", "viridis"]
},
"crystal3dAnimation": {},
"laserPulse": {
@@ -192,8 +192,6 @@
"_super": ["_", "model", "beamElement"],
"title": ["Element Name", "String", "Mirror"]
},
- "plotAnimation": {},
- "plot2Animation": {},
"simulation": {
"distanceFromSource": ["", "Float", 0.0 , "", 0.0]
},
@@ -297,7 +295,8 @@
},
"crystal3dAnimation": {
"title": "Crystal 3D Mesh",
- "advanced": []
+ "advanced": [],
+ "canFullScreen": false
},
"mirror2": {
"title": "Two Mirrors",
@@ -364,14 +363,6 @@
"focalLength"
]
},
- "plotAnimation": {
- "title": "Radial Temperature Profile",
- "advanced": []
- },
- "plot2Animation": {
- "title": "Longitudinal Temperature Profile",
- "advanced": []
- },
"n0n2Plot": {
"title": "",
"advanced": []
diff --git a/sirepo/package_data/template/silas/crystal.py.jinja b/sirepo/package_data/template/silas/crystal.py.jinja
index 2ffe1c4ccc..3e7c37d9eb 100644
--- a/sirepo/package_data/template/silas/crystal.py.jinja
+++ b/sirepo/package_data/template/silas/crystal.py.jinja
@@ -10,32 +10,31 @@ crystal = Crystal(params=PKDict({{crystalParams}}))
sim_type = "{{ thermalCrystal.calc_type }}"
def thermo_optic_sim():
- thermo = ThermoOptic(crystal, {{thermalCrystal.mesh_density}})
+ def _solve():
+ if sim_type == "analytical":
+ return thermo.{{ pump_pulse_profile }}_solution()
- # Initialize a simulator with a crystal object set
- # Set evaluation points & boundary conditions
+ elif sim_type == "fenics":
+ thermo.set_boundary(2*(crystal.radius*100.)**2./40.)
+ thermo.set_load('{{ pump_pulse_profile }}')
+ r = crystal.params.pop_inversion_pump_rep_rate
+ if r <= 1:
+ return thermo.slow_solution('{{ pump_pulse_profile }}')
+ elif r >= 100:
+ return thermo.solve_steady()
+ else:
+ raise AssertionError(f"Invalid rep rate for simulated solve heat_load={'{{ pump_pulse_profile }}'}")
+
+ thermo = ThermoOptic(crystal, {{ thermalCrystal.mesh_density }})
+
+ # prep for 2d solve
thermo.set_points((
{{thermalTransportSettings_grid_points_r}},
{{thermalTransportSettings_grid_points_w}},
{{thermalTransportSettings_grid_points_z}},
- ), {{thermalTransportSettings_edge_fraction}})
- thermo.set_boundary(2*(crystal.radius*100.)**2./40.)
-
- if sim_type == "analytical":
- Ts = thermo.{{ pump_pulse_profile }}_solution()
+ ))
+ res_2d = _solve()
- elif sim_type == "fenics":
- thermo.set_load('{{ pump_pulse_profile }}')
- r = crystal.params.pop_inversion_pump_rep_rate
- if r <= 1:
- Ts = thermo.slow_solution('{{ pump_pulse_profile }}')
- elif r >= 100:
- Ts = thermo.solve_steady()
- else:
- raise AssertionError(f"Invalid rep rate for simulated solve heat_load={'{{ pump_pulse_profile }}'}")
-
- nT, n0Fit, n2Fit = thermo.compute_indices(Ts)
- # Positions of each evaluation point & sets of the unique values
ptzs = thermo.eval_pts[:, 2]
ptrs = (thermo.eval_pts[:, 0] ** 2 + thermo.eval_pts[:, 1] ** 2) ** 0.5
zs = unique(ptzs)
@@ -44,37 +43,38 @@ def thermo_optic_sim():
zface = zs.min() if crystal.params.pop_inversion_pump_type != "right" else zs.max()
# Radial values reflected across axis & meshgrid with longitudinal values
- rrs = array((-rs[::-1]).tolist() + rs[1:].tolist())
- RZ, ZR = meshgrid(rrs, zs)
temp_profile = PKDict(
- radial=[rs, Ts[ptzs==zface]],
- longitudinal=[zs, Ts[ptrs==rcenter]]
+ radial=[rs, res_2d[ptzs==zface]],
+ longitudinal=[zs, res_2d[ptrs==rcenter]]
)
- pTs = Ts.reshape((len(rs), len(zs)), order='F')
+ pTs = res_2d.reshape((len(rs), len(zs)), order='F')
pTs = array((pTs[::-1]).tolist()+pTs[1:].tolist())
- heat_map = pTs
+
+ # prep for 3d solve
+ mesh = thermo.mesh
+ inds = []
+ for item in dolfin.cpp.mesh.facets(mesh):
+ inds.append(item.entities(0).tolist())
+ vertices = mesh.coordinates()
+ thermo.eval_pts = vertices
+ res_3d = _solve()
return PKDict(
thermo=thermo,
- heat_map=heat_map,
+ heat_map=pTs,
temp_profile=temp_profile,
- nT=nT,
- n0Fit=n0Fit,
- n2Fit=n2Fit,
+ intensity=res_3d,
+ vertices=vertices,
+ indices=numpy.array(inds),
)
res = thermo_optic_sim()
-# for 3D plots, get the facets, and build an array
-# containing the indices of their coordinates
-inds = []
-for item in dolfin.cpp.mesh.facets(res.thermo.mesh):
- inds.append(item.entities(0).tolist())
# we will provide these indices to plotly so it can draw proper surfaces
-inds = numpy.array(inds)
-numpy.save("indices.npy", inds)
-numpy.save("vertices.npy", res.thermo.mesh.coordinates())
+numpy.save("indices.npy", res.indices)
+numpy.save("intensity.npy", res.intensity)
+numpy.save("vertices.npy", res.vertices)
template_common.write_dict_to_h5(
res.temp_profile,
"tempProfile.h5"
diff --git a/sirepo/package_data/template/silas/examples/bella.json b/sirepo/package_data/template/silas/examples/bella.json
index e8530a07d5..f060d08514 100644
--- a/sirepo/package_data/template/silas/examples/bella.json
+++ b/sirepo/package_data/template/silas/examples/bella.json
@@ -612,11 +612,8 @@
"reportType": "3d",
"watchpointPlot": "total_intensity"
},
- "plot2Animation": {},
- "plotAnimation": {},
"simulation": {
"distanceFromSource": 0,
- "documentationUrl": "",
"folder": "/Examples",
"name": "100TW amplifier with 3 crystals"
}
diff --git a/sirepo/package_data/template/silas/examples/multiple-pass-single-crystal.json b/sirepo/package_data/template/silas/examples/multiple-pass-single-crystal.json
index b61a33c232..b22b747cce 100644
--- a/sirepo/package_data/template/silas/examples/multiple-pass-single-crystal.json
+++ b/sirepo/package_data/template/silas/examples/multiple-pass-single-crystal.json
@@ -318,12 +318,9 @@
"sigy_waist": 0.003056,
"wfs": ""
},
- "plot2Animation": {},
- "plotAnimation": {},
"simulation": {
"distanceFromSource": 0,
"folder": "/Examples",
- "isExample": true,
"name": "Multiple Passes Through Single Crystal"
}
},
diff --git a/sirepo/package_data/template/silas/examples/single-crystal.json b/sirepo/package_data/template/silas/examples/single-crystal.json
index 9e8024448a..94f82dc55c 100644
--- a/sirepo/package_data/template/silas/examples/single-crystal.json
+++ b/sirepo/package_data/template/silas/examples/single-crystal.json
@@ -67,12 +67,9 @@
"sigy_waist": 0.001,
"wfs": ""
},
- "plot2Animation": {},
- "plotAnimation": {},
"simulation": {
"distanceFromSource": 0,
"folder": "/Examples",
- "isExample": true,
"name": "Single Crystal"
},
"watchpointReport3": {
diff --git a/sirepo/sim_data/silas.py b/sirepo/sim_data/silas.py
index 97e4180a5a..2683549219 100644
--- a/sirepo/sim_data/silas.py
+++ b/sirepo/sim_data/silas.py
@@ -65,8 +65,6 @@ def _compute_model(cls, analysis_model, *args, **kwargs):
"crystal3dAnimation",
"tempProfileAnimation",
"tempHeatMapAnimation",
- "plotAnimation",
- "plot2Animation",
):
return "crystalAnimation"
if "beamlineAnimation" in analysis_model:
diff --git a/sirepo/template/silas.py b/sirepo/template/silas.py
index e6482bd8a8..2678863ebb 100644
--- a/sirepo/template/silas.py
+++ b/sirepo/template/silas.py
@@ -19,7 +19,6 @@
_SIM_DATA, SIM_TYPE, SCHEMA = sirepo.sim_data.template_globals()
-_CRYSTAL_CSV_FILE = "crystal.csv"
_TEMP_PROFILE_FILE = "tempProfile.h5"
_TEMP_HEATMAP_FILE = "tempHeatMap.h5"
_RESULTS_FILE = "results{}.h5"
@@ -309,38 +308,6 @@ def _crystal_animation_percent_complete(run_dir, res, data):
return res
-def _crystal_plot(frame_args):
- x = None
- plots = []
- with open(str(frame_args.run_dir.join(_CRYSTAL_CSV_FILE))) as f:
- for r in csv.reader(f):
- if x is None and r[0] == x_column:
- r.pop(0)
- r.pop(0)
- x = [float(v) * scale for v in r]
- elif r[0] == y_column:
- r.pop(0)
- t = r.pop(0)
- plots.append(
- PKDict(
- points=[float(v) for v in r],
- label="{:.1f} sec".format(float(t)),
- )
- )
- return PKDict(
- title="",
- x_range=[min(x), max(x)],
- y_label="Temperature [°C]",
- x_label=x_heading,
- x_points=x,
- plots=plots,
- y_range=template_common.compute_plot_color_and_range(plots),
- summaryData=PKDict(
- crystalLength=_get_crystal(frame_args.sim_in).length,
- ),
- )
-
-
def _generate_beamline_elements(data):
def _callback(state, element, dz):
if dz:
@@ -425,7 +392,7 @@ def _generate_parameters_file(data):
res, v = template_common.generate_parameters_file(data)
if data.report == "crystalAnimation":
- c = _get_crystal(data)
+ c = data.models.thermalTransportCrystal.crystal
if c.pump_rep_rate <= 1:
c.calc_type = "fenics"
v.crystalParams = PKDict(
@@ -445,7 +412,6 @@ def _generate_parameters_file(data):
)
v.pump_pulse_profile = c.pump_pulse_profile
v.crystalLength = c.length
- v.crystalCSV = _CRYSTAL_CSV_FILE
v.thermalCrystal = c
return res + template_common.render_jinja(SIM_TYPE, v, "crystal.py")
if data.report in _SIM_DATA.SOURCE_REPORTS:
@@ -461,10 +427,6 @@ def _generate_parameters_file(data):
return res + template_common.render_jinja(SIM_TYPE, v)
-def _get_crystal(data):
- return data.models.thermalTransportCrystal.crystal
-
-
def _initial_intensity_percent_complete(run_dir, res, data, model_names):
res.outputInfo = []
if run_dir.join(_RESULTS_FILE.format(0)).exists():
@@ -614,25 +576,6 @@ def gen(self):
raise AssertionError("Report is unavailable")
-def _laser_pulse_report(value_index, filename, title, label):
- values = numpy.load(filename)
- return template_common.parameter_plot(
- values[0].tolist(),
- [
- PKDict(
- points=values[value_index].tolist(),
- label=label,
- ),
- ],
- PKDict(),
- PKDict(
- title=title,
- y_label="",
- x_label="s [m]",
- ),
- )
-
-
def _parse_silas_log(run_dir):
res = ""
path = run_dir.join(template_common.RUN_LOG)