@@ -189,7 +189,6 @@ def get_model_description(filepath: Path, module_name: str, class_name: str) ->
189189 # Produce the xml
190190 return instance .modelName , instance .to_xml ()
191191
192-
193192class FmuBuilder :
194193
195194 @staticmethod
@@ -201,13 +200,31 @@ def build_FMU(
201200 newargs : dict | None = None ,
202201 ** options ,
203202 ) -> Path :
203+ """ Build the FMU from the Python script, additional project files and documentatiion.
204+
205+ Args:
206+ script_file (FilePath): The main Python script containing the Python model class
207+ dest (FilePath)='.': Optional destination path.
208+ If this is a full file name with '.fmu' extension, it is used as FMU file name.
209+ Otherwise the FMU file name is constructed automatically from the script file name.
210+ project_files (Iterable[FilePath]): Optional list/tuple of additional project files needed to run model
211+ documentation_folder (FilePath): Optional additional documentation (beyond modelDescription)
212+ newargs (dict): Optional dict of replacements of model class __init__() arguments.
213+ """
204214 script_file = Path (script_file )
205215 if not script_file .exists ():
206216 raise ValueError (f"No such file { script_file !s} " )
207217 if not script_file .suffix .endswith (".py" ):
208218 raise ValueError (f"File { script_file !s} must have extension '.py'!" )
209-
219+
210220 dest = Path (dest )
221+ if ( dest .suffix == '.fmu' and # explicit FMU file name shall always have suffix '.fmu'
222+ ( dest .is_file () or # Note that .is_file() returns False if the file does not yet exist
223+ not dest .is_dir ())): # if dest represents an (existing) directory we cannot interpret as file!
224+ dest_file = dest
225+ dest = dest .parent
226+ else :
227+ dest_file = "" # FMU file name is automatically generated below
211228 if not dest .exists ():
212229 dest .mkdir (parents = True )
213230 project_files = set (map (Path , project_files ))
@@ -261,13 +278,14 @@ def build_FMU(
261278 temp_dest = temp_dir / file_ .name
262279 shutil .copytree (file_ , temp_dest )
263280 else :
281+ assert file_ .name != script_file .name , ( # avoid the inclusion of the script in project files
282+ "It seems that the script file is included a second time in the project_files" )
264283 shutil .copy2 (file_ , temp_dir )
265284
266285 model_identifier , xml = get_model_description (
267286 temp_dir .absolute () / script_file .name , module_name , model_class .__name__
268287 )
269-
270- dest_file = dest / f"{ model_identifier } .fmu"
288+ dest_file = dest / f"{ model_identifier } .fmu" if dest_file == "" else dest_file
271289
272290 type_node = xml .find ("CoSimulation" )
273291 option_names = [opt .name for opt in FMI2_MODEL_OPTIONS ]
@@ -317,6 +335,8 @@ def build_FMU(
317335 zip_fmu .writestr (
318336 "modelDescription.xml" , xml_str .toprettyxml (encoding = "UTF-8" )
319337 )
338+ if newargs is not None :
339+ sys .modules .pop (Path (script_file ).stem ) # otherwise old script may be active when loading the FMU!
320340
321341 return dest_file
322342
0 commit comments