How to eliminate dead code in Dymola/Modelica - modelica
I am trying to slim down a very complex model to improve performance, and noticed big performance changes when I add or remove variables into the signal bus, especially multi-body frames.
I am wondering if there is any setting that can eliminate code that isn't involved in generating outputs from the model.
I tried setting the bus connector to "protected" to ensure it doesn't become an output but the code to calculate them is still being generated.
I also tried these flags but it doesn't eliminate the dead code:
Advanced.Embedded.OptimizeForOutputs=true;
Advanced.SubstituteVariablesUsedOnce=true;
Evaluate=true;
Advanced.EvaluateAlsoTop=true;
Advanced.SubstituteVariablesUsedOnce=true;
This is a simple model to replicate the scenario:
model TestBusConnector
extends Modelica.Icons.Example;
protected
Modelica.Blocks.Examples.BusUsage_Utilities.Interfaces.ControlBus controlBus
annotation (Placement(transformation(extent={{-20,-20},{20,20}})));
public
Modelica.Blocks.Sources.Sine sine(freqHz=1)
annotation (Placement(transformation(extent={{-40,-50},{-20,-30}})));
Modelica.Blocks.Sources.Constant const(k=0)
annotation (Placement(transformation(extent={{-10,50},{10,70}})));
Modelica.Blocks.Interfaces.RealOutput y
annotation (Placement(transformation(extent={{90,-10},{110,10}})));
equation
connect(y, const.y) annotation (Line(points={{100,0},{60,0},{60,60},{11,60}}, color={0,0,127}));
connect(sine.y, controlBus.testBusVariable)
annotation (Line(points={{-19,-40},{0,-40},{0,0}}, color={0,0,127}));
annotation (experiment(__Dymola_fixedstepsize=0.001, __Dymola_Algorithm="Euler"),
__Dymola_experimentFlags(Advanced(
InlineMethod=0,
InlineOrder=2,
InlineFixedStep=0.001)),
__Dymola_experimentSetupOutput(
states=false,
derivatives=false,
inputs=false,
outputs=false,
auxiliaries=false,
equidistant=false,
events=false));
end TestBusConnector;
Code generated from Dymola 2019 FD01 is shown below:
include <dsblock6.c>
PreNonAliasNew(0)
StartNonAlias(0)
DeclareVariable("sine.amplitude", "Amplitude of sine wave", 1, 0.0,0.0,0.0,0,513)
DeclareVariable("sine.freqHz", "Frequency of sine wave [Hz]", 1, 0.0,0.0,0.0,0,513)
DeclareVariable("sine.phase", "Phase of sine wave [rad|deg]", 0, 0.0,0.0,0.0,0,513)
DeclareVariable("sine.offset", "Offset of output signal", 0, 0.0,0.0,0.0,0,513)
DeclareVariable("sine.startTime", "Output = offset for time < startTime [s]", 0,\
0.0,0.0,0.0,0,513)
DeclareVariable("sine.y", "Connector of Real output signal", 0.0, 0.0,0.0,0.0,0,512)
DeclareVariable("const.k", "Constant output value", 0, 0.0,0.0,0.0,0,513)
DeclareVariable("const.y", "Connector of Real output signal", 0, 0.0,0.0,0.0,0,513)
DeclareOutput("y", "", 0, 0.0, 0.0,0.0,0.0,0,513)
DeclareAlias2("controlBus.testBusVariable", "Connector of Real output signal", \
"sine.y", 1, 5, 5, 1028)
EndNonAlias(0)
#define DymolaHaveUpdateInitVars 1
#include <dsblock5.c>
DYMOLA_STATIC void UpdateInitVars(double*time, double* X_, double* XD_, double* U_, double* DP_, int IP_[], Dymola_bool LP_[], double* F_, double* Y_, double* W_, double QZ_[], double duser_[], int iuser_[], void*cuser_[],struct DYNInstanceData*did_,int initialCall) {
}
StartDataBlock
EndDataBlock
The translated modelica code (dsmodel.mof) still has the calculation for the sine block.
// Translated Modelica model generated by Dymola from Modelica model
// TEMP.TEST.TestBusConnector
// -----------------------------------------------------------------------------
// Initial Section
sine.amplitude := 1;
sine.freqHz := 1;
sine.phase := 0;
sine.offset := 0;
sine.startTime := 0;
const.k := 0;
const.y := 0;
y := 0.0;
// -----------------------------------------------------------------------------
// Conditionally Accepted Section
sine.y := (if time < 0 then 0 else sin(6.283185307179586*time));
// -----------------------------------------------------------------------------
// Eliminated alias variables
// To have eliminated alias variables listed, set
// Advanced.OutputModelicaCodeWithAliasVariables = true
// before translation. May give much output.
Ideally, I would like the model to translate to:
y := 0.0;
The reason the other answers don't work is that your model is not consistent with your question:
"I am wondering if there is any setting that can eliminate code that isn't involved in generating outputs from the model."
By connecting the control-bus to sine.y you implicitly create an output, and thus sine.y is involved in generating outputs from the model.
That can be avoided in one of the following ways:
Remove the connection between sine.y and controlBus
Change controlBus to be protected
Change so that controlBus isn't at the top-level
It's not a direct answer to your question, but still it could help to improve performance. Part of the computational effort you are trying to avoid is generated by computing variables in the result file. This can be avoided by the settings below:
This can be set as an annotation in the model itself using:
annotation (__Dymola_experimentSetupOutput(
states=false,
derivatives=false,
inputs=false,
auxiliaries=false));
There is another flag which could help. It does not give the result you expected, but it might be still useful:
Advanced.Define.AutoRemoveAuxiliaries = true;
The Dymola User Manual 2 describes the flag as follows:
Removes code for auxiliary variables that neither influences the
simulation state nor the outputs. This improves performance a bit.
From this description my expectation was that the code is generated like you asked for, but unfortunately it is not the case.
Related
SystemVerilog not recognizing constant: Error: Range must be bounded by constant expression
In this short example, I want to simplify writing of signal width. With one signal, there is really no need to do this, but in my real code, I have many such signals and declaring them with the longer style wouldn't be appropriate. Could you please enlighten me why I'm getting error for signal_2? module sample #(parameter BYTE_WIDTH = 4); const int BIT_WIDTH = BYTE_WIDTH * 8; logic [BYTE_WIDTH * 8 -1 : 0] signal_1; // works logic [BIT_WIDTH -1 : 0] signal_2; // ** Error: Range must be bounded by constant expressions. endmodule
A const variable is assigned its value at run time. Which is too late: the width of your variable signal_2 needed to be fixed at compile time. So, what you need is a localparam, which (like a parameter) is fixed at compile time, but (unlike a parameter) cannot be overridden from outside: module sample #(parameter BYTE_WIDTH = 4); localparam BIT_WIDTH = BYTE_WIDTH * 8; logic [BYTE_WIDTH * 8 -1 : 0] signal_1; logic [BIT_WIDTH -1 : 0] signal_2; endmodule https://www.edaplayground.com/x/2fKU
Start values as results of previous simulation
Is it possible to use the result of a simulation Sim 1 at time t as start value of simulation Sim 2? The use of extend doesn't work for start values. Example: model Sim 1 Real a; equation a=2*time; end Sim 1; for model Sim 2, I need Real b (start=a at time t) to use in several other set of equations.
You have to differ between the modeling and the simulation process: With the language Modelica you define your models With the simulation tool (like Dymola) you perform the simulation. The keyword extends is part of the Modelica language. So it cannot be of any use in this context, as you use it to define models, not to describe how a simulation should be performed. The solution for your problem must be searched in the simulation tool and Dymola offers a simulator function, which does exactly what you want: simulateExtendedModel. It allows to read the final value of a variable and you can initialize parameters and state variables with it. You can use it in a .mos script or within a Modelica function. So if we rename your models Sim1 and Sim2 to Model1 and Model2 (because they are really models, not simulations) the function below would do what you want: function sim import DymolaCommands.SimulatorAPI.simulateExtendedModel; protected Boolean ok; Real a; Real[1] finalValues; algorithm (ok, finalValues) :=simulateExtendedModel("Model1", 0, 5, finalNames={"a"}); a :=finalValues[1]; simulateExtendedModel("Model2", 5, 10, initialNames={"b"}, initialValues={a}); end sim; If you want to set multiple variables, you can use this code: function sim2 import DymolaCommands.SimulatorAPI.simulateExtendedModel; protected Boolean ok; Real[:] finalValues_sim1; String[:] finalNames_sim1 = {"a1", "a2", "a3"}; String[:] initialNames_sim2 = {"b1", "b2", "b3"}; algorithm (ok, finalValues_sim1) :=simulateExtendedModel("SO.Model1", 0, 5, finalNames=finalNames_sim1); simulateExtendedModel("SO.Model2", 5, 10, initialNames=initialNames_sim2, initialValues=finalValues_sim1); end sim2;
Convert rnorm output of NumericVector with length of 1 to a double?
In the following code I am trying to generate a NumericVector of values from a normal distribution, where every time rnorm() is called each time with a different mean and variance. Here is the code: // [[Rcpp::export]] NumericVector generate_ai(NumericVector log_var) { int log_var_length = log_var.size(); NumericVector temp(log_var_length); for(int i = 0; i < log_var_length; i++) { temp[i] = rnorm(1, -0.5 * log_var[i], sqrt(log_var[i])); } return(temp); } The line that is giving me trouble is this one: temp[i] = rnorm(1, -0.5 * log_var[i], sqrt(log_var[i])); It is causing the error: assigning to 'typename storage_type<14>::type' (aka 'double') from incompatible type 'NumericVector' (aka 'Vector<14>') Since I'm returning one number from rnorm, is there a way to convert this NumericVector return type to a double?
Rcpp provides two methods to access RNG sampling schemes. The first option is a single draw and the second enables n draws using some sweet sweet Rcpp sugar. Under your current setup, you are opting for the later setup. Option 1. Use just the scalar sampling scheme instead of sugar by accessing the RNG function through R::, e.g. temp[i] = R::rnorm(-0.5 * log_var[i], sqrt(log_var[i])); Option 2. Use the subset operator on the NumericVector to obtain the only element. // C++ indices start at 0 instead of 1 temp[i] = Rcpp::rnorm(1, -0.5 * log_var[i], sqrt(log_var[i]))[0]; The prior option will be faster and better. Why you might ask? Well, Option 2 creates a new NumericVector, fills it with a call to Option 1, then requires a subset operation to retrieve the value before assigning it to the desired scalar. In any case, RNG can be a bit confusing. Just make sure to always prefix the function call with the correct namespace (e.g. R:: or Rcpp::) so that you and perhaps future programmers avoid any ambiguity as to what kind of sampling scheme you've opted for. (This is one of the downside of using namespace Rcpp;)
MATLAB: dynamic variable definitions
For a numerical simulation in MATLAB I have parameters defined in an .m file. %; Parameters as simple definitons amb.T = 273.15+25; ... ambient temperature [K] amb.P = 101325; ... ambient pressure [Pa] combustor.T = 273.15+800; ... [K] combustor.P = 100000; ... [Pa] combustor.lambda = 1.1; fuel.x.CH4 = 0.5; ... [0..1] fuel.n = 1; air.x.O2 = 0.21; %; more complex definitions consisting of other params air.P = reactor.P; air.T = amb.T; air.n = fuel.x.CH4 * 2 * fuel.n * combustor.lambda / air.x.O2; Consider this set as 'default' definitions. For running one simulation this definitions works fine. It's getting more complicated if I want to change one of these parameters programmatically for a parameter study (the effect of changing parameters on the results), that is, to perform multiple simulations by using a for loop. In the script performing this I want to change the defintion of several parameters beforehand, i.e. overwrite default definitions. Is there a way to do this without touching the default definitions in-code (comment them/overwrite them literally)? It should be possible to change any parameter in the study-performing script and catch up on default definitions from the listing above (or the other way round). Let me illustrate the problem with the following example: If I want to vary combustor.lambda (let's say running from 0.9 to 1.3) field air.n has to be evaluated again for the change to take place in the actual simulation. So, I could evaluate the listing again, but this way I would lose the study-defined combustor.lambda for the default one. I am thinking about these solutions but I cannot get to how to do this: Use references/handles in a way that the struct fields only hold the definitions, not the actual values. This allows for changing default definitions before 'parsing' the whole struct to get the actual values. Evaluate the default definition set by a function considering (non-default) definitions defined preliminarily, i.e. skipping these lines of the default definition set during evaluation. Any OOP approach. Of course, it is not limited to struct data types, but on the other hand, maybe there are useful functions for structs? Edit: The purpose of the default set is for the programmer to be as free as possible in choosing the varying parameters with any of the other parameters keeping their default definition which can be independent (= values) as well as dependent (= equations like air.n).
% one default parameter set S = struct('T', 25, 'P', 101000, 'lambda', .5, 'fuel', .5); GetNByLambda = #(fuel, lambda) fuel * 2 * lambda; T = struct('P', S.P, 'n', GetNByLambda(S.fuel, S.lambda)); % add more sets S(end+1) = struct('T', 200, 'P', 10000, 'lambda', .8, 'fuel', .7); T(end+1) = struct('P', S.P, 'n', GetNByLambda(S(end+1).fuel, S(end+1).lambda)); % iterate over parameter sets for ii = 1:length(S) disp(S(end+1)) disp(T(end+1)) end
Help with live-updating sound on the iPhone
My question is a little tricky, and I'm not exactly experienced (I might get some terms wrong), so here goes. I'm declaring an instance of an object called "Singer". The instance is called "singer1". "singer1" produces an audio signal. Now, the following is the code where the specifics of the audio signal are determined: OSStatus playbackCallback(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData) { //Singer *me = (Singer *)inRefCon; static int phase = 0; for(UInt32 i = 0; i < ioData->mNumberBuffers; i++) { int samples = ioData->mBuffers[i].mDataByteSize / sizeof(SInt16); SInt16 values[samples]; float waves; float volume=.5; for(int j = 0; j < samples; j++) { waves = 0; waves += sin(kWaveform * 600 * phase)*volume; waves += sin(kWaveform * 400 * phase)*volume; waves += sin(kWaveform * 200 * phase)*volume; waves += sin(kWaveform * 100 * phase)*volume; waves *= 32500 / 4; // <--------- make sure to divide by how many waves you're stacking values[j] = (SInt16)waves; values[j] += values[j]<<16; phase++; } memcpy(ioData->mBuffers[i].mData, values, samples * sizeof(SInt16)); } return noErr; } 99% of this is borrowed code, so I only have a basic understanding of how it works (I don't know about the OSStatus class or method or whatever this is. However, you see those 4 lines with 600, 400, 200 and 100 in them? Those determine the frequency. Now, what I want to do (for now) is insert my own variable in there in place of a constant, which I can change on a whim. This variable is called "fr1". "fr1" is declared in the header file, but if I try to compile I get an error about "fr1" being undeclared. Currently, my technique to fix this is the following: right beneath where I #import stuff, I add the line fr1=0.0;//any number will work properly This sort of works, as the code will compile and singer1.fr1 will actually change values if I tell it to. The problems are now this:A)even though this compiles and the tone specified will play (0.0 is no tone), I get the warnings "Data definition has no type or storage class" and "Type defaults to 'int' in declaration of 'fr1'". I bet this is because for some reason it's not seeing my previous declaration in the header file (as a float). However, again, if I leave this line out the code won't compile because "fr1 is undeclared". B)Just because I change the value of fr1 doesn't mean that singer1 will update the value stored inside the "playbackcallback" variable or whatever is in charge of updating the output buffers. Perhaps this can be fixed by coding differently? C)even if this did work, there is still a noticeable "gap" when pausing/playing the audio, which I need to eliminate. This might mean a complete overhaul of the code so that I can "dynamically" insert new values without disrupting anything. However, the reason I'm going through all this effort to post is because this method does exactly what I want (I can compute a value mathematically and it goes straight to the DAC, which means I can use it in the future to make triangle, square, etc waves easily). I have uploaded Singer.h and .m to pastebin for your veiwing pleasure, perhaps they will help. Sorry, I can't post 2 HTML tags so here are the full links. (http://pastebin.com/ewhKW2Tk) (http://pastebin.com/CNAT4gFv) So, TL;DR, all I really want to do is be able to define the current equation/value of the 4 waves and re-define them very often without a gap in the sound. Thanks. (And sorry if the post was confusing or got off track, which I'm pretty sure it did.)
My understanding is that your callback function is called every time the buffer needs to be re-filled. So changing fr1..fr4 will alter the waveform, but only when the buffer updates. You shouldn't need to stop and re-start the sound to get a change, but you will notice an abrupt shift in the timbre if you change your fr values. In order to get a smooth transition in timbre, you'd have to implement something that smoothly changes the fr values over time. Tweaking the buffer size will give you some control over how responsive the sound is to your changing fr values. Your issue with fr being undefined is due to your callback being a straight c function. Your fr variables are declared as objective-c instance variables as part of your Singer object. They are not accessible by default. take a look at this project, and see how he implements access to his instance variables from within his callback. Basically he passes a reference to his instance to the callback function, and then accesses instance variables through that. https://github.com/youpy/dowoscillator notice: Sinewave *sineObject = inRefCon; float freq = sineObject.frequency * 2 * M_PI / samplingRate; and: AURenderCallbackStruct input; input.inputProc = RenderCallback; input.inputProcRefCon = self; Also, you'll want to move your callback function outside of your #implementation block, because it's not actually part of your Singer object. You can see this all in action here: https://github.com/coryalder/SineWaver